From 3f873d656cccf3ea28ff98e9ff05c7e4d6bef9af Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 7 Sep 2026 12:22:28 +0100 Subject: [PATCH] Check for non allocateable registers and disallow them to be pinned to output parameters --- .../arm64/tablegen/cpp-compiler/cpp-gen.odin | 9 +++++++++ .../riscv/tablegen/cpp-compiler/cpp-gen.odin | 8 ++++++++ .../x86/tablegen/cpp-compiler/cpp-gen.odin | 9 +++++++++ src/asm_tables_amd64.cpp | 9 +++++++++ src/asm_tables_arm64.cpp | 9 +++++++++ src/asm_tables_riscv.cpp | 8 ++++++++ src/check_asm.cpp | 20 +++++++++++++++++++ src/llvm_backend_asm.cpp | 2 +- 8 files changed, 73 insertions(+), 1 deletion(-) diff --git a/core/rexcode/isa/arm64/tablegen/cpp-compiler/cpp-gen.odin b/core/rexcode/isa/arm64/tablegen/cpp-compiler/cpp-gen.odin index 099e19f9e..781541470 100644 --- a/core/rexcode/isa/arm64/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/arm64/tablegen/cpp-compiler/cpp-gen.odin @@ -630,6 +630,15 @@ main :: proc() { bool supports_memory_index_not_just_disp() const { return true; // base + (optionally extended/shifted) index register } + bool reg_is_non_allocateable(Register r) const { + switch (r) { + case REG_XZR: + case REG_SP: + case REG_NZCV: case REG_DAIF: case REG_FPCR: case REG_FPSR: + return true; + } + return false; + } """) strings.write_string(&sb, "\n\n") diff --git a/core/rexcode/isa/riscv/tablegen/cpp-compiler/cpp-gen.odin b/core/rexcode/isa/riscv/tablegen/cpp-compiler/cpp-gen.odin index 84cb4d67a..74af7fd97 100644 --- a/core/rexcode/isa/riscv/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/riscv/tablegen/cpp-compiler/cpp-gen.odin @@ -625,6 +625,14 @@ main :: proc() { bool supports_memory_index_not_just_disp() const { return false; } + + bool reg_is_non_allocateable(Register r) const { + switch (r) { + case REG_ZERO: + return true; + } + return false; + } """) strings.write_string(&sb, "\n\n") diff --git a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin index 7e78e220e..4e45efe5d 100644 --- a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin @@ -619,6 +619,15 @@ main :: proc() { bool supports_memory_index_not_just_disp() const { return true; } + + bool reg_is_non_allocateable(Register r) const { + switch (r) { + case REG_CR0: case REG_CR2: case REG_CR3: case REG_CR4: case REG_CR8: + case REG_CS: case REG_DS: case REG_ES: case REG_FS: case REG_GS: case REG_SS: + return true; + } + return false; + } """) strings.write_string(&sb, "\n\n") diff --git a/src/asm_tables_amd64.cpp b/src/asm_tables_amd64.cpp index 3a37baa78..0ab2969a4 100644 --- a/src/asm_tables_amd64.cpp +++ b/src/asm_tables_amd64.cpp @@ -647,6 +647,15 @@ struct Asm_amd64 { return true; } + bool reg_is_non_allocateable(Register r) const { + switch (r) { + case REG_CR0: case REG_CR2: case REG_CR3: case REG_CR4: case REG_CR8: + case REG_CS: case REG_DS: case REG_ES: case REG_FS: case REG_GS: case REG_SS: + return true; + } + return false; + } + AsmOperandKind kind_from_operand_type(OperandType type) const { switch (type) { case OP_R8: case OP_R16: case OP_R32: case OP_R64: diff --git a/src/asm_tables_arm64.cpp b/src/asm_tables_arm64.cpp index 4ea4100e4..5f0757334 100644 --- a/src/asm_tables_arm64.cpp +++ b/src/asm_tables_arm64.cpp @@ -848,6 +848,15 @@ struct Asm_arm64 { bool supports_memory_index_not_just_disp() const { return true; // base + (optionally extended/shifted) index register } + bool reg_is_non_allocateable(Register r) const { + switch (r) { + case REG_XZR: + case REG_SP: + case REG_NZCV: case REG_DAIF: case REG_FPCR: case REG_FPSR: + return true; + } + return false; + } AsmOperandKind kind_from_operand_type(OperandType type) const { switch (type) { diff --git a/src/asm_tables_riscv.cpp b/src/asm_tables_riscv.cpp index d108305ab..28cd91ff3 100644 --- a/src/asm_tables_riscv.cpp +++ b/src/asm_tables_riscv.cpp @@ -599,6 +599,14 @@ struct Asm_riscv { return false; } + bool reg_is_non_allocateable(Register r) const { + switch (r) { + case REG_ZERO: + return true; + } + return false; + } + AsmOperandKind kind_from_operand_type(OperandType type) const { switch (type) { case OP_NONE: diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 64dbbf6ab..dd1cf49d2 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -2928,6 +2928,26 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity error(previous_prefix_instr, "A prefix must be immediately followed by an instruction, but the template ended"); } + + for (auto const &ed : ate->decls) { + if (ed.param_group != AsmTemplateEntityDeclParamGroup_Output) { + continue; + } + if (ed.tie >= 0 || ed.no_init) { + continue; + } + if (ed.pin.len != 0) { + auto r = asm_ctx->register_lookup(ed.pin); + if (asm_ctx->reg_is_non_allocateable(r)) { + error(ed.entity->token, + "'asm' output '%.*s' is pinned to a register '%%%.*s' which cannot be an output; " + "read it into a general-purpose register in the body instead " + "(e.g. on AMD64, 'mov %%rax, %%%.*s' with the output pinned to %%rax)", + LIT(ed.entity->token.string), LIT(ed.pin), LIT(ed.pin)); + } + } + } + // NOTE(bill): After the linear collection pass of the mnemonics, // now do the CFG building, analysis, and liveness checks (only if everything was correct) diff --git a/src/llvm_backend_asm.cpp b/src/llvm_backend_asm.cpp index a7e0a2424..772f02094 100644 --- a/src/llvm_backend_asm.cpp +++ b/src/llvm_backend_asm.cpp @@ -1,4 +1,4 @@ -#define LLVM_ASM_DEBUG_PRINT false +#define LLVM_ASM_DEBUG_PRINT true struct lbAsmGenerate { Entity * tmpl_entity;