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 09c6cd4c7..1dbff1890 100644 --- a/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin +++ b/core/rexcode/isa/x86/tablegen/cpp-compiler/cpp-gen.odin @@ -187,6 +187,18 @@ main :: proc() { strings.write_string(&sb, "\t\tbool writes_mem;\n") strings.write_string(&sb, "\t\tbool reads_mem;\n") strings.write_string(&sb, "\t\tSideEffectFlags side_effects;\n") + strings.write_string(&sb, "\n") + strings.write_string(&sb, "\t\tbool implies_clobber_cc() {\n") + strings.write_string(&sb, "\t\t\tu16 const CC_MASK = ClobberFlag_CF|ClobberFlag_PF|ClobberFlag_AF|\n") + strings.write_string(&sb, "\t\t\t ClobberFlag_ZF|ClobberFlag_SF|ClobberFlag_OF;\n") + strings.write_string(&sb, "\t\t\treturn ((flags_wr | flags_undef) & CC_MASK) != 0;\n") + strings.write_string(&sb, "\t\t}\n") + strings.write_string(&sb, "\t\tbool implies_clobber_memory() {\n") + strings.write_string(&sb, "\t\t\treturn writes_mem || reads_mem ||\n") + strings.write_string(&sb, "\t\t\t\t(side_effects & (SideEffectFlag_FENCE |\n") + strings.write_string(&sb, "\t\t\t\t SideEffectFlag_CACHE |\n") + strings.write_string(&sb, "\t\t\t\t SideEffectFlag_SERIALIZING)) != 0;\n") + strings.write_string(&sb, "\t\t}\n") strings.write_string(&sb, "\t};\n") } strings.write_string(&sb, "\n"); diff --git a/src/asm_tables_amd64.cpp b/src/asm_tables_amd64.cpp index 3d4b35ba2..c5a722f14 100644 --- a/src/asm_tables_amd64.cpp +++ b/src/asm_tables_amd64.cpp @@ -192,6 +192,18 @@ struct Asm_amd64 { bool writes_mem; bool reads_mem; SideEffectFlags side_effects; + + bool implies_clobber_cc() { + u16 const CC_MASK = ClobberFlag_CF|ClobberFlag_PF|ClobberFlag_AF| + ClobberFlag_ZF|ClobberFlag_SF|ClobberFlag_OF; + return ((flags_wr | flags_undef) & CC_MASK) != 0; + } + bool implies_clobber_memory() { + return writes_mem || reads_mem || + (side_effects & (SideEffectFlag_FENCE | + SideEffectFlag_CACHE | + SideEffectFlag_SERIALIZING)) != 0; + } }; static u16 const register_codes[REG_COUNT]; diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 7da9e22e4..b40e3b167 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -734,7 +734,9 @@ gb_internal AsmOperandKind determine_asm_operand_kind(Operand const *operand) { template -gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInstruction *instr, u16 mnemonic, Slice const &operands, u8 previous_prefix, Ast *previous_prefix_instr) { +gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tmpl_entity, AstAsmInstruction *instr, + u16 mnemonic, Slice const &operands, + u8 previous_prefix, Ast *previous_prefix_instr) { GB_ASSERT(mnemonic > 0); auto forms = asm_ctx->encoding_forms(mnemonic); String name = asm_ctx->mnemonic_strings[mnemonic]; @@ -860,6 +862,15 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInst error(instr->name, "Asm prefix cannot be applied to '%.*s'", LIT(name)); } } + + GB_ASSERT(tmpl_entity->kind == Entity_AsmTemplate); + + // Handle clobbering from mnemonic + auto clobber = asm_ctx->clobber(mnemonic); + + tmpl_entity->AsmTemplate.clobber_cc |= clobber.implies_clobber_cc(); + tmpl_entity->AsmTemplate.clobber_memory |= clobber.implies_clobber_memory(); + tmpl_entity->AsmTemplate.has_side_effects |= clobber.side_effects != 0; return; } @@ -1260,12 +1271,10 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity entity->type = type; + auto *clobber_registers_set = &entity->AsmTemplate.clobber_registers_set; + check_asm_specs(ctx, ate->param_scope, at->specs, &ate->decls); { // check clobbers - StringSet reg_set = {}; - string_set_init(®_set, 16); - defer (string_set_destroy(®_set)); - bool clobber_cc = false; bool clobber_memory = false; @@ -1276,7 +1285,7 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity String reg = asm_reg->name.string; Operand operand = {}; if (check_register(asm_ctx, &operand, asm_reg)) { - if (string_set_update(®_set, reg)) { + if (string_set_update(clobber_registers_set, reg)) { error(clobber->value, "#clobber %%%.*s has already been defined", LIT(reg)); } } @@ -1302,6 +1311,9 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity break; } } + + entity->AsmTemplate.clobber_cc = clobber_cc; + entity->AsmTemplate.clobber_memory = clobber_memory; } // collect label decls @@ -1363,7 +1375,7 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity previous_prefix = cast(u8)mnemonic; previous_prefix_instr = instruction_; } else if (res == CheckMnemomic_Mnemonic) { - check_mnemonic(asm_ctx, ctx, instr, mnemonic, slice_from_array(operands), previous_prefix, previous_prefix_instr); + check_mnemonic(asm_ctx, ctx, entity, instr, mnemonic, slice_from_array(operands), previous_prefix, previous_prefix_instr); previous_prefix = 0; previous_prefix_instr = nullptr; } else { diff --git a/src/entity.cpp b/src/entity.cpp index 6a5991857..e5f31eb7b 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -345,7 +345,7 @@ struct Entity { bool clobber_cc; bool clobber_memory; - Array clobber_registers; + StringSet clobber_registers_set; Scope *param_scope; Scope *label_scope; diff --git a/src/llvm_backend_asm.cpp b/src/llvm_backend_asm.cpp index 530106ed2..108baab73 100644 --- a/src/llvm_backend_asm.cpp +++ b/src/llvm_backend_asm.cpp @@ -403,6 +403,7 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate { } } + bool memory_clobbered_already = false; // Pass 3: clobbers // Only the Scratch group. Unpinned register scratch was already emitted as an // output in Pass 1, so it is skipped here. @@ -427,12 +428,27 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate { break; case AsmTemplateEntityDecl_Memory: // general memory clobber raw("~{memory}"); + memory_clobbered_already = true; break; default: GB_PANIC("asm: invalid scratch operand kind"); } } + // Template-level clobbers derived from #clobber cc / #clobber memory. + if (tmpl_entity->AsmTemplate.clobber_cc) { + sep(); + if (build_context.metrics.arch == TargetArch_amd64) { + raw("~{flags}"); // x86 EFLAGS condition codes + } else { + raw("~{cc}"); // AArch64 uses ~{cc} + } + } + if (tmpl_entity->AsmTemplate.clobber_memory && memory_clobbered_already) { + sep(); + raw("~{memory}"); + } + // Build the callee type // NOTE(bill): Even though the user has given a signature, this might not actually match what // LLVM requires it to be due to the scratch parameters and more, so many of the results might