Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions lib_nn/src/asm/lookup8.S
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,103 @@ FUNCTION_NAME:

#endif

#if defined(__VX4B__)

/*
void lookup8_asm(
uint8_t* Y,
const uint8_t* X,
const uint8_t* lut,
const unsigned elm_start,
const unsigned elm_count);
*/

#define FUNCTION_NAME lookup8_asm

.p2align 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is sufficient for to be legal, but you may want to increase it.
at 2B alignment, because the function starts with a 4B instruction (a packet), the branching to it will always FNOP if this lands on a 2B (but not 4B) boundary.
You could increase it further to make the function FNOP-predictable or potentially FNOP-free, though you would need to consider the offset of the branch targets. I don't know if FNOP predictability is an aim of lib_nn in general.

.globl FUNCTION_NAME
.type FUNCTION_NAME,@function

#define STACK_X18 (0)
#define STACK_X19 (STACK_X18 + 4)
#define STACK_X20 (STACK_X19 + 4)
#define STACK_X21 (STACK_X20 + 4)
#define STACK_X22 (STACK_X21 + 4)
#define STACK_X23 (STACK_X22 + 4)
#define STACK_X24 (STACK_X23 + 4)
#define STACK_VEC1_TMP (STACK_X24 + 4)
#define STACK_RA (STACK_VEC1_TMP + 32)
#define NSTACKBYTES (STACK_RA + 4)
#define ROUND_UP(X, Y) (((X+Y-1)/Y)*Y)

#define out_data x10
#define in_data x11
#define lut x12
#define start x13
#define count x18
#define mask x19
#define counter x20
#define thirty_two x21
#define vec1_tmp x22

FUNCTION_NAME:

{ xm.entsp ROUND_UP(NSTACKBYTES, 16) ; xm.nop }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{ xm.entsp ROUND_UP(NSTACKBYTES, 16) ; xm.nop }
xm.entsp ROUND_UP(NSTACKBYTES, 16)

The packed nop isn't needed as we don't have issue modes any more. (The only - rare - case you might do this is to force alignment of later instructions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI nop (rather than xm.nop) is a standard mnemonic which will compress to a short instruction - so it's a bit more idiomatic if you do need a nop.

xm.stdsp x18, x19, 0*8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally speaking, the ABI register naming is preferred to the numeric naming - e.g. xm.stdsp s2, s3, 0*8.

xm.stdsp x20, x21, 1*8
xm.stdsp x22, x23, 2*8
sw x24, 3*8(sp)
addi count, x14, 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mv count, x14 is more idiomatic.

add out_data, out_data, start
add in_data, in_data, start
addi vec1_tmp, sp, STACK_VEC1_TMP
xm.mkmski mask, 32
li thirty_two, 32
// out_data substract 32 first for optimize in the loop
sub out_data, out_data, thirty_two
Comment on lines +151 to +159

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like most of this sequence could be packetised.


// Set VPU to 8-bit mode
li x28, 512
{ xm.vsetc x28 ; xm.shri counter, count, 5 }
xm.vldc lut

FUNCTION_NAME.loop:
{ xm.vldr in_data ; add in_data, in_data, thirty_two }
xm.vlookup lut, 0, 1
xm.vlookup lut, 1, 2
xm.vlookup lut, 2, 3
xm.vlookup lut, 3, 4
xm.vlookup lut, 4, 5
xm.vlookup lut, 5, 6
xm.vlookup lut, 6, 7
xm.vlookup lut, 7, 0
bnez counter, FUNCTION_NAME.save

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bnez counter, FUNCTION_NAME.save
xm.bt counter, FUNCTION_NAME.save

Saves a couple of bytes and I think you could speculatively packetise the xm.zexti with it.

// In last loop - make mask for remaining elements
xm.zexti count, 4
xm.mkmsk mask, count

FUNCTION_NAME.save:
{ xm.vstd vec1_tmp ; xm.nop }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xm.nop probably isn't needed.

{ xm.vldr vec1_tmp ; add out_data, out_data, thirty_two }
xm.vstrpv out_data, mask
{ addi counter, counter, -1 ; xm.bt counter, FUNCTION_NAME.loop }


xm.lddsp x18, x19, 0*8
xm.lddsp x20, x21, 1*8
xm.lddsp x22, x23, 2*8
lw x24, 3*8(sp)

{xm.retsp ROUND_UP(NSTACKBYTES, 16); xm.nop}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xm.nop not required.


.size FUNCTION_NAME, . -FUNCTION_NAME
.resource_const FUNCTION_NAME, "stack_frame_bytes", ROUND_UP(NSTACKBYTES, 16)
.resource_list_empty FUNCTION_NAME, "callees"
.resource_list_empty FUNCTION_NAME, "tail_callees"
.resource_list_empty FUNCTION_NAME, "parallel_callees"

#endif




15 changes: 13 additions & 2 deletions lib_nn/src/c/nn_operator.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ void lookup8_ref(uint8_t *Y, const uint8_t *X, const uint8_t *lut,
}
}

#if defined(__XS3A__) || defined(__VX4B__)
void lookup8_asm(
uint8_t* Y,
const uint8_t* X,
const uint8_t* lut,
const unsigned elm_start,
const unsigned elm_count);
#endif

#ifdef NN_USE_REF
void requantize_16_to_8(int8_t *y, const int16_t *x, const unsigned elm_start,
const unsigned elm_count) {
Expand All @@ -148,10 +157,12 @@ void requantize_16_to_8(int8_t *y, const int16_t *x, const unsigned elm_start,

void lookup8(uint8_t *Y, const uint8_t *X, const uint8_t *lut,
const unsigned elm_start, const unsigned elm_count) {
#if defined(NN_USE_REF) || defined(__VX4A__) || defined(__VX4B__)
#if defined(NN_USE_REF)
lookup8_ref(Y, X, lut, elm_start, elm_count);
#elif defined(__XS3A__)
#elif defined(__XS3A__) || defined(__VX4B__)
lookup8_asm(Y, X, lut, elm_start, elm_count);
#else
lookup8_ref(Y, X, lut, elm_start, elm_count);
#endif // NN_USE_REF
}

Expand Down