mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-14 01:34:35 +00:00
Support [base + index<<scale] and [base + index>>scale]; Support syntax for [base]:u8 (not implemented yet)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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, ")");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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; }) \
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user