diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 12bdc5f56..070da2ba6 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -740,10 +740,10 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInst } { - if (best_score == operands.count*2 - 1) { - error(instr->name, "Asm operands to '%.*s' nearly matched the expected encoding forms", LIT(name)); + if (best_score >= gb_max(operands.count*2 - 2, 0)) { + error(instr->name, "'%.*s' operands nearly matched the expected encoding forms", LIT(name)); } else { - error(instr->name, "Asm operands to '%.*s' matched none of the expected encoding forms", LIT(name)); + error(instr->name, "'%.*s' operands matched none of the expected encoding forms", LIT(name)); } for_array(i, valid_spots) { if (valid_spots[i] || i >= operands.count) { @@ -760,33 +760,27 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, AstAsmInst i32 bits_required = 0; check_asm_immediate_value_fits(ev, want_bits[i], &bits_required, nullptr); if (bits_required > 0) { - error(operands[i].expr, - "Operand %td of '%.*s' is a %d-bit immediate value, but the value %s does not fit in the %d-bit immediate this form encodes", - i, LIT(name), bits_required, vs, cast(int)want_bits[i]); + error(operands[i].expr, "'%.*s' operand-%td is a %d-bit immediate value, but the value %s does not fit in the %d-bit immediate this form encodes", + LIT(name), i, bits_required, vs, cast(int)want_bits[i]); } else { - error(operands[i].expr, - "Operand %td of '%.*s' is an immediate value, but the value %s does not fit in the %d-bit immediate this form encodes", - i, LIT(name), vs, cast(int)want_bits[i]); + error(operands[i].expr, "'%.*s' operand-%td is an immediate value, but the value %s does not fit in the %d-bit immediate this form encodes", + LIT(name), i, vs, cast(int)want_bits[i]); } gb_string_free(vs); } else if (m == AsmMismatch_ImmType) { - error(operands[i].expr, - "Operand %td of '%.*s': a floating-point constant cannot be used as an immediate", - i, LIT(name)); + error(operands[i].expr, "'%.*s'' operand-%td a floating-point constant cannot be used as an immediate", + LIT(name), i); } else if (m == AsmMismatch_Size && want_bits[i] && got_bits[i]) { - error(operands[i].expr, - "Operand %td of '%.*s' has the wrong size: expected a %u-bit operand, got %u-bit", - i, LIT(name), cast(unsigned)want_bits[i], cast(unsigned)got_bits[i]); + error(operands[i].expr, "'%.*s' operand-%td has the wrong size: expected a %u-bit operand, got %u-bit", + LIT(name), i, cast(unsigned)want_bits[i], cast(unsigned)got_bits[i]); } else if (m == AsmMismatch_Class) { - error(operands[i].expr, - "Operand %td of '%.*s' is in the wrong register class, expected %.*s operand, got %.*s", - i, LIT(name), LIT(asm_operand_kind_expected_strings[dst]), LIT(asm_operand_kind_expected_strings[src])); + error(operands[i].expr, "'%.*s' operand-%td is in the wrong register class, expected %.*s operand, got %.*s", + LIT(name), i, LIT(asm_operand_kind_expected_strings[dst]), LIT(asm_operand_kind_expected_strings[src])); } else if (dst) { - error(operands[i].expr, - "Operand %td of '%.*s' has an invalid kind, expected %.*s operand", - i, LIT(name), LIT(asm_operand_kind_expected_strings[dst])); + error(operands[i].expr, "'%.*s' operand-%td has an invalid kind, expected %.*s operand", + LIT(name), i, LIT(asm_operand_kind_expected_strings[dst])); } else { - error(operands[i].expr, "Operand %td of '%.*s' has an invalid kind", i, LIT(name)); + error(operands[i].expr, "'%.*s' operand-%td has an invalid kind", LIT(name), i); } } } @@ -905,11 +899,21 @@ gb_internal void check_asm_instruction_operand(AsmCtx *asm_ctx, CheckerContext * break; } if (scale.mode == Addressing_Constant) { + gbString s = exact_value_to_string(scale.value); + defer (gb_string_free(s)); if (scale.value.kind != ExactValue_Integer) { - gbString s = exact_value_to_string(scale.value); error(scale.expr, "A scale must be a constant integer or an immediate, got %s", s); - gb_string_free(s); break; + } else { + i64 v = exact_value_to_i64(scale.value); + switch (v) { + case 1: case 2: case 4: case 8: + // okay + 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); + break; + } } } else { Entity *param_entity = entity_of_node(scale.expr); diff --git a/src/llvm_backend_asm.cpp b/src/llvm_backend_asm.cpp index eae026648..3ba6c5cd3 100644 --- a/src/llvm_backend_asm.cpp +++ b/src/llvm_backend_asm.cpp @@ -3,6 +3,14 @@ struct lbAsmGenerate { AstAsmTemplate * tmpl_node; Array *ops; + enum WriteOperandFlags : u32 { + WriteOperandFlag_PrintPrefixes = 1<<0, + WriteOperandFlag_IsScale = 1<<1, + + WriteOperandFlag_NONE = 0, + WriteOperandFlag_DEFAULT = WriteOperandFlag_PrintPrefixes, + }; + void init(Entity *entity) { this->tmpl_entity = entity; GB_ASSERT(this->tmpl_entity != nullptr); @@ -34,7 +42,7 @@ struct lbAsmGenerate { return nullptr; } - gbString write_operand(gbString asm_string, Array op_number, Ast *op, bool print_prefixes=true) { + gbString write_operand(gbString asm_string, Array op_number, Ast *op, u32 flags) { switch (op->kind) { case_ast_node(i, Ident, op); Entity *e = entity_of_node(op); @@ -45,7 +53,7 @@ struct lbAsmGenerate { asm_string = gb_string_append_fmt(asm_string, "$%d", idx); case_end; case_ast_node(mem_op, AsmMemoryOperand, op); - asm_string = this->write_memory_operand(asm_string, op_number, mem_op, false); + asm_string = this->write_memory_operand(asm_string, op_number, mem_op, flags&~WriteOperandFlag_PrintPrefixes); case_end; case_ast_node(bl, BasicLit, op); @@ -55,11 +63,23 @@ struct lbAsmGenerate { switch (ev.kind) { case ExactValue_Integer: { String s = big_int_to_string(heap_allocator(), &ev.value_integer, 10); - if (print_prefixes) { + 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); + + 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)); + break; + } + } break; } case ExactValue_Float: @@ -100,24 +120,24 @@ struct lbAsmGenerate { }; - virtual gbString write_memory_operand(gbString asm_string, Array const &op_number, AstAsmMemoryOperand *mem_op, bool print_prefixes=true) = 0; + virtual gbString write_memory_operand(gbString asm_string, Array const &op_number, AstAsmMemoryOperand *mem_op, u32 flags) = 0; virtual lbValue emit_call(lbProcedure *p, Array const &args) = 0; }; struct lbAsmGenerate_amd64 : lbAsmGenerate { - gbString write_memory_operand(gbString asm_string, Array const &op_number, AstAsmMemoryOperand *mem_op, bool print_prefixes=true) override { + gbString write_memory_operand(gbString asm_string, Array const &op_number, AstAsmMemoryOperand *mem_op, u32 flags) override { if (mem_op->disp) { - asm_string = this->write_operand(asm_string, op_number, mem_op->disp, /*print_prefixes*/false); + asm_string = this->write_operand(asm_string, op_number, mem_op->disp, flags&~WriteOperandFlag_PrintPrefixes); } 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); + asm_string = this->write_operand(asm_string, op_number, mem_op->base, flags); if (mem_op->index) { asm_string = gb_string_appendc(asm_string, ","); - asm_string = this->write_operand(asm_string, op_number, mem_op->index); + 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, /*print_prefixes*/false); + asm_string = this->write_operand(asm_string, op_number, mem_op->scale, (flags|WriteOperandFlag_IsScale)&~WriteOperandFlag_PrintPrefixes); } } asm_string = gb_string_appendc(asm_string, ")"); @@ -273,7 +293,7 @@ struct lbAsmGenerate_amd64 : lbAsmGenerate { if (j < instr->operands.count-1) { asm_string = gb_string_appendc(asm_string, ", "); } - asm_string = this->write_operand(asm_string, op_number, op); + asm_string = this->write_operand(asm_string, op_number, op, WriteOperandFlag_DEFAULT); } case_end; case_ast_node(label, AsmLabelDecl, instr_);