diff --git a/src/arch/avr/asm.c b/src/arch/avr/asm.c index 7b67a15c..31ee2dc5 100644 --- a/src/arch/avr/asm.c +++ b/src/arch/avr/asm.c @@ -188,8 +188,22 @@ static long io_name(const char *s, int len) * inline asm statement does. */ int avrasm_is_word(const char *stmt, const char *w, int len) { + static const char *const op[] = { + "lo8", "hi8", "hlo8", "hh8", + "pm_lo8", "pm_hi8", "pm_hh8", "gs" + }; (void)stmt; - return io_name(w, len) >= 0; + if (io_name(w, len) >= 0) + return 1; + /* These are expression operators, not symbols. Without naming them + * here, gas.c records (for example) `lo8` as the first undefined symbol + * in a multi-statement inline template, then cannot relocate the real + * `call` or `jmp` symbol that follows it. */ + for (unsigned i = 0; i < sizeof op / sizeof op[0]; i++) + if ((int)strlen(op[i]) == len && + strncmp(w, op[i], (size_t)len) == 0) + return 1; + return 0; } static long expr_prim(const char **p, struct expr_ctx *x) diff --git a/tests/golden/avr-inline-asm.sh b/tests/golden/avr-inline-asm.sh index 441f2a91..94556209 100755 --- a/tests/golden/avr-inline-asm.sh +++ b/tests/golden/avr-inline-asm.sh @@ -74,6 +74,29 @@ printf 'unsigned int f(unsigned char a, unsigned char b)\n'\ with 'clr r1' is the idiom -- refusing it forbids using mul at all:" head -4 "$out/m.err"; exit 1; } +# A constant byte-selection expression in one statement must not change how +# the following statement's symbol is scanned. The 18-character names are +# the two EmbLinkRTOS AVR entry points that exposed the cut at 16 characters. +cat > "$out/long-symbol.c" <<'EOF' +void emb_arch_switch_to(void); +void embk_thread_launch(void); +void f(void) +{ + __asm__ volatile("ldi r18, lo8(2303)\n\t" + "call emb_arch_switch_to\n\t" + "jmp embk_thread_launch" ::: "r18"); +} +EOF +"$EMBCC" --target=avr -Os -c "$out/long-symbol.c" \ + -o "$out/long-symbol.o" 2> "$out/long-symbol.err" || { + echo "lo8() made a following long symbol fail to assemble:" + cat "$out/long-symbol.err"; exit 1; } +for s in emb_arch_switch_to embk_thread_launch; do + llvm-readelf -r "$out/long-symbol.o" | grep -q "$s" || { + echo "the inline asm's long symbol was cut: $s" + llvm-readelf -r "$out/long-symbol.o"; exit 1; } +done + QEMU=${EMBCC_QEMU_AVR:-qemu-system-avr} command -v "$QEMU" >/dev/null 2>&1 || { echo "the refusals hold (SKIP: no $QEMU to run the rest)"; exit 0; }