mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-14 09:44:26 +00:00
Fix pinning logic for asm registers
This commit is contained in:
@@ -2155,6 +2155,22 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice<Ast *>
|
||||
|
||||
bool must_check_value = false;
|
||||
|
||||
String pin = {};
|
||||
if (spec->value != nullptr) {
|
||||
if (spec->value->kind != Ast_AsmRegister) {
|
||||
gbString s = expr_to_string(spec->value);
|
||||
error(spec->value, "Expected an asm register, got %s", s);
|
||||
gb_string_free(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_node(reg, AsmRegister, spec->value);
|
||||
pin = reg->name.string;
|
||||
if (pin == "any") {
|
||||
pin = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (spec->tied_name == nullptr) {
|
||||
if (spec->type != nullptr) {
|
||||
Type *type = check_type(ctx, spec->type);
|
||||
@@ -2175,6 +2191,7 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice<Ast *>
|
||||
AsmTemplateEntityDecl ed = asm_template_entity_decl_default(entity);
|
||||
ed.param_group = AsmTemplateEntityDeclParamGroup_Scratch;
|
||||
ed.total_index = cast(i32)asm_template_entity_decls->count;
|
||||
ed.pin = pin;
|
||||
array_add(asm_template_entity_decls, ed);
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
@@ -2188,6 +2205,17 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice<Ast *>
|
||||
} else if (input == nullptr) {
|
||||
error(spec->name, "Undefined parameter declaration '%.*s'", LIT(spec->name->Ident.token.string));
|
||||
continue;
|
||||
} else {
|
||||
i32 index = -1;
|
||||
auto group = check_asm_find_group(input, *asm_template_entity_decls, &index);
|
||||
gb_unused(group);
|
||||
GB_ASSERT(index >= 0);
|
||||
auto *i = &(*asm_template_entity_decls)[index];
|
||||
if (i->pin.len == 0) {
|
||||
i->pin = pin;
|
||||
} else {
|
||||
error(spec_, "Asm register has already been pinned");
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -2230,13 +2258,12 @@ gb_internal void check_asm_specs(CheckerContext *ctx, Scope *scope, Slice<Ast *>
|
||||
i->tie = output_index;
|
||||
o->tie = input_index;
|
||||
|
||||
i->pin = pin;
|
||||
o->pin = pin;
|
||||
|
||||
|
||||
must_check_value = true;
|
||||
}
|
||||
|
||||
if (spec->value != nullptr) {
|
||||
// TODO(bill): check registers
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
return pos;
|
||||
};
|
||||
|
||||
// ---- Pass 1: outputs -----------------------------------------------------
|
||||
// Pass 1: outputs
|
||||
// Real outputs plus *unpinned* register scratch (modeled as discarded
|
||||
// early-clobber outputs, since a clobber can only name a fixed register).
|
||||
for_array(i, ops) {
|
||||
@@ -163,37 +163,32 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
|
||||
sep();
|
||||
|
||||
if (false && e.kind == AsmTemplateEntityDecl_Memory) {
|
||||
// Indirect memory output: writes through a pointer, so it takes an arg
|
||||
// and contributes nothing to the return type.
|
||||
raw("=*m");
|
||||
lbValue ptr = args[e.param_index];
|
||||
unsigned pos = add_arg(ptr.value);
|
||||
array_add(&elem_attrs, ElemAttr{pos, lb_type(m, type_deref(ptr.type))});
|
||||
op_number[i] = next_op++;
|
||||
} else {
|
||||
// Register output: '=' ['&'] ( '{pin}' | class-letter )
|
||||
raw("=");
|
||||
if (is_alloc_scratch) raw("&"); // early-clobber: keep scratch off any input reg
|
||||
if (e.pin.len != 0) { raw("{"); put(e.pin); raw("}"); }
|
||||
else raw(class_letter(e.reg_class));
|
||||
|
||||
// Use the entity's real declared type so the return-struct slot matches
|
||||
// the constraint's width/class (e.g. <4 x float> for a #simd[4]f32 scratch).
|
||||
LLVMTypeRef ty = is_alloc_scratch
|
||||
? lb_type(m, e.entity->type)
|
||||
: output_llvm_type(e);
|
||||
|
||||
ret_slot[i] = cast(i32)ret_types.count;
|
||||
array_add(&ret_types, ty);
|
||||
op_number[i] = next_op++;
|
||||
// Register output: '=' ['&'] ( '{pin}' | class-letter )
|
||||
raw("=");
|
||||
if (is_alloc_scratch) { // early-clobber: keep scratch off any input reg
|
||||
raw("&");
|
||||
}
|
||||
if (e.pin.len != 0) {
|
||||
raw("{"); put(e.pin); raw("}");
|
||||
} else {
|
||||
raw(class_letter(e.reg_class));
|
||||
}
|
||||
|
||||
// Use the entity's real declared type so the return-struct slot matches
|
||||
// the constraint's width/class (e.g. <4 x float> for a #simd[4]f32 scratch).
|
||||
LLVMTypeRef ty = is_alloc_scratch ? lb_type(m, e.entity->type) : output_llvm_type(e);
|
||||
|
||||
ret_slot[i] = cast(i32)ret_types.count;
|
||||
array_add(&ret_types, ty);
|
||||
op_number[i] = next_op++;
|
||||
}
|
||||
|
||||
// ---- Pass 2: inputs ------------------------------------------------------
|
||||
// Pass 2: inputs
|
||||
for (isize i = 0; i < ops.count; i++) {
|
||||
AsmTemplateEntityDecl const &e = ops[i];
|
||||
if (e.param_group != AsmTemplateEntityDeclParamGroup_Input) continue;
|
||||
if (e.param_group != AsmTemplateEntityDeclParamGroup_Input) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sep();
|
||||
lbValue v = args[e.param_index];
|
||||
@@ -209,8 +204,11 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
switch (e.kind) {
|
||||
case AsmTemplateEntityDecl_Register:
|
||||
case AsmTemplateEntityDecl_Memory:
|
||||
if (e.pin.len != 0) { raw("{"); put(e.pin); raw("}"); }
|
||||
else raw(class_letter(e.reg_class));
|
||||
if (e.pin.len != 0) {
|
||||
raw("{"); put(e.pin); raw("}");
|
||||
} else {
|
||||
raw(class_letter(e.reg_class));
|
||||
}
|
||||
add_arg(v.value);
|
||||
break;
|
||||
// case AsmTemplateEntityDecl_Memory: {
|
||||
@@ -230,7 +228,7 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
op_number[i] = next_op++;
|
||||
}
|
||||
|
||||
// ---- Build the template text ---------------------------------------------
|
||||
// Build the template text
|
||||
GB_ASSERT(tmpl.node->kind == Ast_AsmTemplate);
|
||||
auto *node = &tmpl.node->AsmTemplate;
|
||||
for_array(i, node->instructions) {
|
||||
@@ -263,7 +261,7 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Pass 3: clobbers ----------------------------------------------------
|
||||
// Pass 3: clobbers
|
||||
// Only the Scratch group. Unpinned register scratch was already emitted as an
|
||||
// output in Pass 1, so it is skipped here.
|
||||
for (isize i = 0; i < ops.count; i++) {
|
||||
@@ -289,7 +287,10 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Build the callee type -----------------------------------------------
|
||||
// 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
|
||||
// need to be completely ignored to match the user's given signature.
|
||||
LLVMTypeRef ret_ty = nullptr;
|
||||
if (ret_types.count == 0) {
|
||||
ret_ty = LLVMVoidTypeInContext(ctx);
|
||||
@@ -301,21 +302,27 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
|
||||
LLVMTypeRef fn_ty = LLVMFunctionType(ret_ty, param_types.data, cast(unsigned)param_types.count, /*vararg*/false);
|
||||
|
||||
// TODO(bill): determine all the cases when side-effects happen
|
||||
bool has_side_effects = tmpl.has_side_effects;
|
||||
|
||||
LLVMValueRef ia = LLVMGetInlineAsm(
|
||||
fn_ty,
|
||||
asm_string, cast(size_t)gb_string_length(asm_string),
|
||||
constraints, cast(size_t)gb_string_length(constraints),
|
||||
/*HasSideEffects*/ tmpl.has_side_effects,
|
||||
/*HasSideEffects*/ has_side_effects,
|
||||
/*IsAlignStack*/ tmpl.is_align_stack,
|
||||
LLVMInlineAsmDialectATT,
|
||||
/*CanThrow*/ false);
|
||||
|
||||
LLVMValueRef call = LLVMBuildCall2(p->builder, fn_ty, ia, call_args.data, cast(unsigned)call_args.count, "");
|
||||
|
||||
{
|
||||
{ // DEBUG PRINT!!!
|
||||
// DEBUG PRINT!!!
|
||||
// DEBUG PRINT!!!
|
||||
// DEBUG PRINT!!!
|
||||
gb_printf_err("%s\n", asm_string);
|
||||
char *ir = LLVMPrintValueToString(call);
|
||||
gb_printf_err("%s\n", ir);
|
||||
gb_printf_err("%s\n\n", ir);
|
||||
LLVMDisposeMessage(ir);
|
||||
}
|
||||
|
||||
@@ -326,7 +333,7 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
LLVMAddCallSiteAttribute(call, cast(LLVMAttributeIndex)(elem_attrs[k].arg_pos + 1), attr);
|
||||
}
|
||||
|
||||
// ---- Repackage results in Odin result order ------------------------------
|
||||
// Repackage results in Odin result order
|
||||
Type *pt = base_type(entity->type);
|
||||
isize result_count = (pt->Proc.results != nullptr) ? pt->Proc.results->Tuple.variables.count : 0;
|
||||
if (result_count == 0) {
|
||||
@@ -340,8 +347,12 @@ gb_internal lbValue lb_emit_asm_template_call(lbProcedure *p, Entity *entity, Ar
|
||||
|
||||
for (isize i = 0; i < ops.count; i++) {
|
||||
AsmTemplateEntityDecl const &e = ops[i];
|
||||
if (e.param_group != AsmTemplateEntityDeclParamGroup_Output) continue;
|
||||
if (e.result_index < 0) continue; // memory output: not a returned value
|
||||
if (e.param_group != AsmTemplateEntityDeclParamGroup_Output) {
|
||||
continue;
|
||||
}
|
||||
if (e.result_index < 0) {
|
||||
continue; // memory output: not a returned value
|
||||
}
|
||||
GB_ASSERT(ret_slot[i] >= 0);
|
||||
|
||||
LLVMValueRef v = (ret_types.count == 1)
|
||||
|
||||
@@ -542,6 +542,7 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
|
||||
n->AsmLabelDecl.name = clone_ast(n->AsmLabelDecl.name, f);
|
||||
break;
|
||||
case Ast_AsmInstruction:
|
||||
n->AsmInstruction.prefix = clone_ast(n->AsmInstruction.prefix, f);
|
||||
n->AsmInstruction.name = clone_ast(n->AsmInstruction.name, f);
|
||||
n->AsmInstruction.operands = clone_ast_array(n->AsmInstruction.operands, f);
|
||||
break;
|
||||
@@ -2628,8 +2629,12 @@ gb_internal Ast *parse_asm_template(AstFile *f) {
|
||||
value = parse_asm_register(f);
|
||||
}
|
||||
|
||||
if (type == nullptr && value == nullptr) {
|
||||
syntax_error(f->curr_token, "An asm specification must specify at least either a type or a value");
|
||||
if (tied_name != nullptr) {
|
||||
if (type != nullptr) {
|
||||
syntax_error(f->curr_token, "An asm specification for tied values cannot declare a type");
|
||||
}
|
||||
} else if (type == nullptr && value == nullptr) {
|
||||
syntax_error(f->curr_token, "An asm specification must specify at least either a type or a value if the value is not tied");
|
||||
}
|
||||
|
||||
spec = alloc_ast_node(f, Ast_AsmSpec);
|
||||
|
||||
@@ -497,6 +497,7 @@ struct AstSplitArgs {
|
||||
Ast * name; \
|
||||
}) \
|
||||
AST_KIND(AsmInstruction, "asm instruction", struct { \
|
||||
Ast * prefix; /*optional*/ \
|
||||
Ast * name; \
|
||||
Slice<Ast *> operands; \
|
||||
}) \
|
||||
|
||||
Reference in New Issue
Block a user