From fd9ccd7b10f49983e2b4977cc16d84a04e1e4004 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 18 Aug 2026 14:44:24 +0100 Subject: [PATCH] Support segment overrides on `asm` memory operands --- src/check_asm.cpp | 39 ++++++++++++++++++++++++++++++++++++++- src/llvm_backend_asm.cpp | 17 ++++++++++++++--- src/parser.cpp | 33 +++++++++++++++++++++++---------- src/parser.hpp | 1 + 4 files changed, 76 insertions(+), 14 deletions(-) diff --git a/src/check_asm.cpp b/src/check_asm.cpp index ee79c240d..1b1cc4bd9 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -1098,6 +1098,26 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext * break; } + Operand segment_override = {}; + check_asm_instruction_operand(asm_ctx, ctx, entity, &segment_override, mem_op->segment_override, false); + + if (segment_override.expr == nullptr) { + // okay + } else if (segment_override.expr->kind == Ast_AsmRegister) { + String reg_name = segment_override.expr->AsmRegister.name.string; + auto reg = asm_ctx->register_lookup(reg_name); + auto reg_class = asm_ctx->reg_class(asm_ctx->register_codes[reg]); + if (reg_class != asm_ctx->REG_CLASS_SEG) { + gbString s = expr_to_string(segment_override.expr); + error(segment_override.expr, "A segment override must be a selector register parameter, got %s", s); + gb_string_free(s); + } + } else { + gbString s = expr_to_string(segment_override.expr); + error(segment_override.expr, "A segment override must be a selector register parameter, got %s", s); + gb_string_free(s); + } + Operand base = {}; Operand index = {}; Operand scale = {}; @@ -1107,7 +1127,7 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext * check_asm_instruction_operand(asm_ctx, ctx, entity, &scale, mem_op->scale, false); check_asm_instruction_operand(asm_ctx, ctx, entity, &disp, mem_op->disp, false); - // NOTE(bill): if the index is actually an immediate and there is no scale nor disp, + // NOTE(bill): if the base/index is actually an immediate and there is no scale nor disp, // then treat it as a disp, and modify the AST too if (index.expr != nullptr && scale.expr == nullptr && disp.expr == nullptr) { bool do_swap = index.mode == Addressing_Constant; @@ -1129,6 +1149,23 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext * mem_op->index_op = {}; } } + if (base.expr != nullptr && index.expr == nullptr && scale.expr == nullptr && disp.expr == nullptr) { + bool do_swap = base.mode == Addressing_Constant; + if (!do_swap) { + Entity *param_entity = entity_of_node(base.expr); + if (param_entity != nullptr && param_entity->kind == Entity_Variable) { + auto kind = check_asm_find_kind(param_entity, ate->decls); + do_swap = kind == AsmTemplateEntityDecl_Immediate; + } + } + if (do_swap) { + disp = base; + base = {}; + + mem_op->disp = mem_op->base; + mem_op->base = nullptr; + } + } i32 base_w = 0; i32 index_w = 0; diff --git a/src/llvm_backend_asm.cpp b/src/llvm_backend_asm.cpp index b77cf83ae..2e5e93d00 100644 --- a/src/llvm_backend_asm.cpp +++ b/src/llvm_backend_asm.cpp @@ -146,6 +146,11 @@ struct lbAsmGenerate { case_ast_node(label, AsmLabelDecl, op); asm_string = write_label(asm_string, &label->name->Ident); case_end; + + case_ast_node(reg, AsmRegister, op); + asm_string = gb_string_appendc(asm_string, "%"); + asm_string = gb_string_append_length(asm_string, reg->name.string.text, reg->name.string.len); + case_end; default: GB_PANIC("TODO(bill): write_operand for '%s'", expr_to_string(op)); break; @@ -287,6 +292,11 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate { gbString write_memory_operand(gbString asm_string, Slice const &op_number, AstAsmMemoryOperand *mem_op, u32 flags) override { + if (mem_op->segment_override != nullptr) { + asm_string = this->write_operand(asm_string, op_number, mem_op->segment_override, flags); + asm_string = gb_string_appendc(asm_string, ":"); + } + if (mem_op->disp) { u32 disp_flags = flags; disp_flags &= ~WriteOperandFlag_PrintPrefixes; @@ -297,8 +307,9 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate { asm_string = this->write_operand(asm_string, op_number, mem_op->disp, disp_flags); } asm_string = gb_string_appendc(asm_string, "("); - GB_ASSERT(mem_op->base != nullptr); - asm_string = this->write_operand(asm_string, op_number, mem_op->base, flags); + if (mem_op->base != nullptr) { + asm_string = this->write_operand(asm_string, op_number, mem_op->base, flags); + } if (mem_op->index) { u32 index_flags = flags; if (mem_op->index_op.kind == Token_Sub) { @@ -657,7 +668,7 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate { LLVMValueRef call = LLVMBuildCall2(p->builder, fn_ty, ia, call_args.data, cast(unsigned)call_args.count, ""); - if (true) { + if (false) { // DEBUG PRINT!!! // DEBUG PRINT!!! // DEBUG PRINT!!! diff --git a/src/parser.cpp b/src/parser.cpp index cb84d7e10..543e89d0e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -548,6 +548,7 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { n->AsmInstruction.operands = clone_ast_array(n->AsmInstruction.operands, f); break; case Ast_AsmMemoryOperand: + n->AsmMemoryOperand.segment_override = clone_ast(n->AsmMemoryOperand.segment_override, f); n->AsmMemoryOperand.base = clone_ast(n->AsmMemoryOperand.base, f); n->AsmMemoryOperand.index = clone_ast(n->AsmMemoryOperand.index, f); n->AsmMemoryOperand.scale = clone_ast(n->AsmMemoryOperand.scale, f); @@ -2483,6 +2484,7 @@ gb_internal Ast *parse_asm_operand(AstFile *f, bool allow_memory_operand) { case Token_OpenBracket: if (allow_memory_operand) { Token open = expect_token(f, Token_OpenBracket); + Ast *segment_override = nullptr; Ast *base = nullptr; Ast *index = nullptr; Ast *scale = nullptr; @@ -2495,6 +2497,16 @@ gb_internal Ast *parse_asm_operand(AstFile *f, bool allow_memory_operand) { base = parse_asm_operand(f, false); + if (allow_token(f, Token_Colon)) { + // [segment: ...] + segment_override = base; + if (segment_override != nullptr && + segment_override->kind != Ast_AsmRegister) { + error(segment_override, "Expected an asm register as the segment override"); + } + base = parse_asm_operand(f, false); + } + // [base] // [base + index] // [base - index] @@ -2526,16 +2538,17 @@ gb_internal Ast *parse_asm_operand(AstFile *f, bool allow_memory_operand) { } Ast *mem = alloc_ast_node(f, Ast_AsmMemoryOperand); - mem->AsmMemoryOperand.open = open; - mem->AsmMemoryOperand.base = base; - mem->AsmMemoryOperand.index_op = index_op; - mem->AsmMemoryOperand.index = index; - mem->AsmMemoryOperand.scale_op = scale_op; - mem->AsmMemoryOperand.scale = scale; - mem->AsmMemoryOperand.disp_op = disp_op; - mem->AsmMemoryOperand.disp = disp; - mem->AsmMemoryOperand.close = close; - mem->AsmMemoryOperand.type = type; + mem->AsmMemoryOperand.open = open; + mem->AsmMemoryOperand.segment_override = segment_override; + mem->AsmMemoryOperand.base = base; + mem->AsmMemoryOperand.index_op = index_op; + mem->AsmMemoryOperand.index = index; + mem->AsmMemoryOperand.scale_op = scale_op; + mem->AsmMemoryOperand.scale = scale; + mem->AsmMemoryOperand.disp_op = disp_op; + mem->AsmMemoryOperand.disp = disp; + mem->AsmMemoryOperand.close = close; + mem->AsmMemoryOperand.type = type; return mem; } diff --git a/src/parser.hpp b/src/parser.hpp index 8b6b72278..2dfb8be21 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -510,6 +510,7 @@ struct AstSplitArgs { }) \ AST_KIND(AsmMemoryOperand, "asm memory operand", struct { \ Token open; \ + Ast * segment_override; \ Ast * base; \ Token index_op; \ Ast * index; \