From fdd6cece3004a2cb56a1b4d376ff80a594ba56de Mon Sep 17 00:00:00 2001 From: Rangi Date: Fri, 4 Sep 2026 10:42:29 -0400 Subject: [PATCH] Warn about signed 8-bit truncation for `add sp, e8` and `ld hl, sp + e8` --- include/asm/rpn.hpp | 2 ++ src/asm/parser.y | 14 +++++++++++--- src/asm/rpn.cpp | 19 +++++++++++++++++++ test/asm/sp-signed-truncation.asm | 22 ++++++++++++++++++++++ test/asm/sp-signed-truncation.err | 16 ++++++++++++++++ 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 test/asm/sp-signed-truncation.asm create mode 100644 test/asm/sp-signed-truncation.err diff --git a/include/asm/rpn.hpp b/include/asm/rpn.hpp index 2a0a341c..3685174f 100644 --- a/include/asm/rpn.hpp +++ b/include/asm/rpn.hpp @@ -57,10 +57,12 @@ struct Expression { void addCheckBitIndex(uint8_t mask); void checkNBit(uint8_t n) const; + void checkSignedNBit(uint8_t n) const; void encode(std::vector &buffer) const; }; bool checkNBit(int32_t v, uint8_t n, char const *name); +bool checkSignedNBit(int32_t v, uint8_t n, char const *name); #endif // RGBDS_ASM_RPN_HPP diff --git a/src/asm/parser.y b/src/asm/parser.y index 1f801952..dbe8c6e4 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -340,6 +340,7 @@ %type reloc_3bit %type reloc_8bit %type reloc_16bit +%type reloc_8bit_signed // Constant numbers %type iconst @@ -1241,6 +1242,13 @@ reloc_8bit: } ; +reloc_8bit_signed: + relocexpr { + $$ = std::move($1); + $$.checkSignedNBit(8); + } +; + reloc_16bit: relocexpr { $$ = std::move($1); @@ -1886,7 +1894,7 @@ sm83_add: | SM83_ADD MODE_HL COMMA reg_ss { sect_ConstByte(0x09 | ($4 << 4)); } - | SM83_ADD MODE_SP COMMA reloc_8bit { + | SM83_ADD MODE_SP COMMA reloc_8bit_signed { sect_ConstByte(0xE8); sect_RelByte($4, 1); } @@ -2434,11 +2442,11 @@ op_a_n: op_sp_offset: OP_ADD relocexpr { $$ = std::move($2); - $$.checkNBit(8); + $$.checkSignedNBit(8); } | OP_SUB relocexpr { $$.makeUnaryOp(RPN_NEG, std::move($2)); - $$.checkNBit(8); + $$.checkSignedNBit(8); } | %empty { ::error("\"LD HL, SP\" is not a valid instruction; use \"LD HL, SP + 0\""); diff --git a/src/asm/rpn.cpp b/src/asm/rpn.cpp index 0c956af4..e0c29f3e 100644 --- a/src/asm/rpn.cpp +++ b/src/asm/rpn.cpp @@ -512,6 +512,25 @@ bool checkNBit(int32_t v, uint8_t n, char const *name) { return true; } +// Checks that an RPN expression's value fits within N bits (must be signed) +void Expression::checkSignedNBit(uint8_t n) const { + if (isKnown()) { + ::checkSignedNBit(value(), n, nullptr); + } +} + +bool checkSignedNBit(int32_t v, uint8_t n, char const *name) { + assume(n != 0); // That doesn't make sense + assume(n < CHAR_BIT * sizeof(int) - 1); // Otherwise `1 << n` is UB + + if (v < -(1 << (n - 1)) || v >= 1 << (n - 1)) { + warning(WARNING_TRUNCATION_1, "%s must be signed %u-bit", name ? name : "Expression", n); + return false; + } + + return true; +} + void Expression::encode(std::vector &buffer) const { assume(buffer.empty()); diff --git a/test/asm/sp-signed-truncation.asm b/test/asm/sp-signed-truncation.asm new file mode 100644 index 00000000..b209d8fb --- /dev/null +++ b/test/asm/sp-signed-truncation.asm @@ -0,0 +1,22 @@ +section "test", rom0 + +opt Wtruncation=1 + +; good +ld hl, sp + 0 +ld hl, sp + 127 +ld hl, sp - 128 +ld hl, sp + -128 +add sp, 0 +add sp, 127 +add sp, -128 + +; bad +ld hl, sp + 128 +ld hl, sp - 129 +ld hl, sp + 255 +ld hl, sp - 256 +add sp, 128 +add sp, -129 +add sp, 255 +add sp, -256 diff --git a/test/asm/sp-signed-truncation.err b/test/asm/sp-signed-truncation.err new file mode 100644 index 00000000..53d83624 --- /dev/null +++ b/test/asm/sp-signed-truncation.err @@ -0,0 +1,16 @@ +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(15) +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(16) +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(17) +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(18) +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(19) +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(20) +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(21) +warning: Expression must be signed 8-bit [-Wtruncation] + at sp-signed-truncation.asm(22)