From b6c64c28b56a9534162d2a384d375766d151f255 Mon Sep 17 00:00:00 2001 From: kalsprite Date: Sun, 23 Aug 2026 22:36:57 -0700 Subject: [PATCH] Diagnose in an asm template instead of aborting with SIGILL --- src/check_asm.cpp | 7 ++++++- tests/issues/run.bat | 1 + tests/issues/run.sh | 10 ++++++++++ tests/issues/test_issue_asm_rip_register.odin | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/issues/test_issue_asm_rip_register.odin diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 53e758092..db89a29b9 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -825,6 +825,11 @@ gb_internal bool check_register(AsmCtx *asm_ctx, Operand *operand, AstAsmRegiste u16 width_in_bits = asm_ctx->reg_size(r); switch (width_in_bits) { + case 0: + // a register whose class the width table cannot describe, `%rip` being the only one. + // anchored on the name rather than the operand, which clobbers and pins do not have + error(asm_reg->name, "Asm registers with no operand width are not supported: %%%.*s", LIT(name)); + return false; case 8: operand->type = t_u8; break; @@ -838,7 +843,7 @@ gb_internal bool check_register(AsmCtx *asm_ctx, Operand *operand, AstAsmRegiste operand->type = t_u64; break; case 80: - error(operand->expr, "80-bit width asm registers are not supported"); + error(asm_reg->name, "80-bit width asm registers are not supported"); return false; case 128: operand->type = alloc_type_simd_vector(4, t_f32); diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 4cbb424b4..ec261fdc7 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -46,6 +46,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused ..\..\..\odin check ..\test_issue_ellipsis_type_call.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "10" || exit /b ..\..\..\odin check ..\test_issue_foreign_redeclaration.odin -no-entry-point %COMMON% || exit /b ..\..\..\odin check ..\test_issue_foreign_redeclaration_mismatch.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b +..\..\..\odin check ..\test_issue_asm_rip_register.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "6" || exit /b ..\..\..\odin build ..\test_issue_7037.odin %COMMON% -o:none || exit /b ..\..\..\odin build ..\test_issue_7188.odin %COMMON% || exit /b clang -c ..\test_issue_sysv_abi.c -o test_issue_sysv_abi_c.o || exit /b diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 98c144d9c..3bd80dbef 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -110,6 +110,16 @@ else exit 1 fi +# `asm` templates are amd64-only, so this file is empty on every other architecture +if [[ "$(uname -m)" == "x86_64" || "$(uname -m)" == "amd64" ]]; then + if [[ $($ODIN check ../test_issue_asm_rip_register.odin -no-entry-point $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 6 ]]; then + echo "SUCCESSFUL 1/1" + else + echo "SUCCESSFUL 0/1" + exit 1 + fi +fi + if [[ $($ODIN build ../test_issue_7108.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]]; then echo "SUCCESSFUL 1/1" else diff --git a/tests/issues/test_issue_asm_rip_register.odin b/tests/issues/test_issue_asm_rip_register.odin new file mode 100644 index 000000000..8b9784790 --- /dev/null +++ b/tests/issues/test_issue_asm_rip_register.odin @@ -0,0 +1,15 @@ +#+build amd64 +// `%rip` is in the amd64 register table, but its class carries no width, so the checker's width +// switch reached its `GB_PANIC` default arm and aborted with SIGILL instead of diagnosing. Every +// position that can name a register reached it, including `[%rip + disp]`. +package test_issues + +rip_src :: asm() -> (v: u64) { mov v, %rip; } +rip_dst :: asm(x: u64) { mov %rip, x; } +rip_mem :: asm() { mov %rax, [%rip + 8]; } +rip_clob :: asm() [#clobber %rip] { nop; } +rip_in :: asm(x: u64) [x = %rip] { nop; } +rip_out :: asm() -> (r: u64) [r = %rip] { nop; } + +// the nearest special-purpose register that does carry a class has to keep checking cleanly +rsp_ok :: asm() -> (v: u64) { mov v, %rsp; }