Support [base + index<<scale] and [base + index>>scale]; Support syntax for [base]:u8 (not implemented yet)

This commit is contained in:
gingerBill
2026-08-12 09:03:31 +01:00
parent 21ad041232
commit c80fedc070
5 changed files with 103 additions and 27 deletions

View File

@@ -991,14 +991,40 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *
break;
} else {
i64 v = exact_value_to_i64(scale.value);
switch (v) {
case 1: case 2: case 4: case 8:
// okay
Token op = mem_op->scale_op;
switch (op.kind) {
case Token_Mul:
switch (v) {
case 1: case 2: case 4: case 8:
// okay
break;
default:
error(scale.expr, "A scale using '*' must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %s", s);
break;
}
break;
case Token_Shl:
case Token_Shr:
switch (v) {
case 0: case 1: case 2: case 3:
// okay
break;
default:
error(scale.expr, "A shifting scale using '%.*s' must be a constant integer or an immediate with the value 0, 1, 2, or 3, got %s", LIT(op.string), s);
break;
}
break;
default:
error(scale.expr, "A scale must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %s", s);
error(op, "Unknown/unhandled scaling operator '%.*s'", LIT(op.string));
break;
}
if (op.kind == Token_Shr) {
if (build_context.metrics.arch != TargetArch_arm64) {
error(op, "The target platform does not support '%.*s' for shifting scale parameters in memory operands", LIT(op.string));
}
}
}
} else {
Entity *param_entity = entity_of_node(scale.expr);
@@ -1065,6 +1091,20 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext *
}
}
if (mem_op->type) {
Type *type_interpretation = check_type(ctx, mem_op->type);
if (type_interpretation != nullptr && type_interpretation != t_invalid) {
operand->type = alloc_type_pointer(type_interpretation);
if (!is_valid_asm_parameter_type(type_interpretation) ||
is_type_pointer(type_interpretation)) { // do not allow pointers even if they are valid asm parameter types
gbString s = type_to_string(type_interpretation);
error(mem_op->type, "Asm memory operands type interpretation must be either an integer, boolean, float, or #simd vector, got %s", s);
gb_string_free(s);
}
}
}
return;
case_end;
case_ast_node(label, AsmLabelDecl, expr);

View File

@@ -6,6 +6,7 @@ struct lbAsmGenerate {
enum WriteOperandFlags : u32 {
WriteOperandFlag_PrintPrefixes = 1<<0,
WriteOperandFlag_IsScale = 1<<1,
WriteOperandFlag_IsScaleLog2 = 1<<2,
WriteOperandFlag_NONE = 0,
WriteOperandFlag_DEFAULT = WriteOperandFlag_PrintPrefixes,
@@ -62,24 +63,33 @@ struct lbAsmGenerate {
GB_ASSERT(ev.kind != ExactValue_Invalid);
switch (ev.kind) {
case ExactValue_Integer: {
String s = big_int_to_string(heap_allocator(), &ev.value_integer, 10);
if (flags & WriteOperandFlag_PrintPrefixes) {
asm_string = gb_string_appendc(asm_string, "$$");
}
asm_string = gb_string_append_length(asm_string, s.text, s.len);
gb_free(heap_allocator(), s.text);
i64 val = exact_value_to_i64(ev);
if (flags & WriteOperandFlag_IsScale) {
i64 val = exact_value_to_i64(ev);
switch (val) {
case 1: case 2: case 4: case 8:
// okay
break;
default:
error(op, "A scale must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %.*s", LIT(s));
error(op, "A scale must be a constant integer or an immediate with the value 1, 2, 4, or 8, got %lld", cast(long long)val);
break;
}
} else if (flags & WriteOperandFlag_IsScaleLog2) {
switch (val) {
case 0: case 1: case 2: case 3:
// NOTE(bill): AMD64 only supports full scales
val = (cast(i64)1)<<val;
break;
default:
error(op, "A shifting scale must be a constant integer or an immediate with the value 0, 1, 2, or 3, got %lld", cast(long long)val);
break;
}
}
if (flags & WriteOperandFlag_PrintPrefixes) {
asm_string = gb_string_appendc(asm_string, "$$");
}
asm_string = gb_string_append_fmt(asm_string, "%d", cast(int)val);
break;
}
case ExactValue_Float:
@@ -137,7 +147,15 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate {
asm_string = this->write_operand(asm_string, op_number, mem_op->index, flags);
if (mem_op->scale) {
asm_string = gb_string_appendc(asm_string, ",");
asm_string = this->write_operand(asm_string, op_number, mem_op->scale, (flags|WriteOperandFlag_IsScale)&~WriteOperandFlag_PrintPrefixes);
switch (mem_op->scale_op.kind) {
case Token_Mul:
asm_string = this->write_operand(asm_string, op_number, mem_op->scale, (flags|WriteOperandFlag_IsScale)&~WriteOperandFlag_PrintPrefixes);
break;
case Token_Shl:
case Token_Shr:
asm_string = this->write_operand(asm_string, op_number, mem_op->scale, (flags|WriteOperandFlag_IsScaleLog2)&~WriteOperandFlag_PrintPrefixes);
break;
}
}
}
asm_string = gb_string_appendc(asm_string, ")");

View File

@@ -551,6 +551,7 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->AsmMemoryOperand.index = clone_ast(n->AsmMemoryOperand.index, f);
n->AsmMemoryOperand.scale = clone_ast(n->AsmMemoryOperand.scale, f);
n->AsmMemoryOperand.disp = clone_ast(n->AsmMemoryOperand.disp, f);
n->AsmMemoryOperand.type = clone_ast(n->AsmMemoryOperand.type, f);
break;
}
return n;
@@ -2444,12 +2445,18 @@ gb_internal Ast *parse_asm_operand(AstFile *f, bool allow_memory_operand) {
Ast *index = nullptr;
Ast *scale = nullptr;
Ast *disp = nullptr;
Ast *type = nullptr;
Token scale_op = {};
base = parse_asm_operand(f, false);
if (allow_token(f, Token_Add)) {
Ast *possible_index = parse_asm_operand(f, false);
if (allow_token(f, Token_Mul)) {
if (allow_token(f, Token_Mul) ||
allow_token(f, Token_Shl) ||
allow_token(f, Token_Shr)) {
scale_op = f->prev_token;
index = possible_index;
scale = parse_asm_operand(f, false);
if (allow_token(f, Token_Add)) {
@@ -2462,13 +2469,19 @@ gb_internal Ast *parse_asm_operand(AstFile *f, bool allow_memory_operand) {
Token close = expect_token(f, Token_CloseBracket);
if (allow_token(f, Token_Colon)) {
type = parse_type(f);
}
Ast *mem = alloc_ast_node(f, Ast_AsmMemoryOperand);
mem->AsmMemoryOperand.open = open;
mem->AsmMemoryOperand.base = base;
mem->AsmMemoryOperand.index = index;
mem->AsmMemoryOperand.scale = scale;
mem->AsmMemoryOperand.disp = disp;
mem->AsmMemoryOperand.close = close;
mem->AsmMemoryOperand.open = open;
mem->AsmMemoryOperand.base = base;
mem->AsmMemoryOperand.index = index;
mem->AsmMemoryOperand.scale = scale;
mem->AsmMemoryOperand.scale_op = scale_op;
mem->AsmMemoryOperand.disp = disp;
mem->AsmMemoryOperand.close = close;
mem->AsmMemoryOperand.type = type;
return mem;
}

View File

@@ -502,12 +502,14 @@ struct AstSplitArgs {
Slice<Ast *> operands; \
}) \
AST_KIND(AsmMemoryOperand, "asm memory operand", struct { \
Token open; \
Ast * base; \
Ast * index; \
Ast * scale; \
Ast * disp; \
Token close; \
Token open; \
Ast * base; \
Ast * index; \
Token scale_op; \
Ast * scale; \
Ast * disp; \
Ast * type; \
Token close; \
}) \
AST_KIND(_ExprBegin, "", bool) \
AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \

View File

@@ -196,6 +196,9 @@ Token ast_end_token(Ast *node) {
}
return ast_end_token(node->AsmInstruction.name);
case Ast_AsmMemoryOperand:
if (node->AsmMemoryOperand.type) {
return ast_end_token(node->AsmMemoryOperand.type);
}
return node->AsmMemoryOperand.close;
case Ast_BadExpr: return node->BadExpr.end;