mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-01 10:52:19 +00:00
Inline asm expression (-llvm-api)
See https://llvm.org/docs/LangRef.html#inline-assembler-expressions Example: ``` x := asm(i32) -> i32 { "bswap $0", "=r,r", }(123); ``` Allowed directives `#side_effect`, `#align_stack`, `#att`, `#intel` e.g. `asm() #side_effect #intel {...}`
This commit is contained in:
@@ -79,7 +79,11 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
|
||||
}
|
||||
t = default_type(t);
|
||||
}
|
||||
if (is_type_polymorphic(t)) {
|
||||
if (is_type_asm_proc(t)) {
|
||||
error(e->token, "Invalid use of inline asm in %.*s", LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return nullptr;
|
||||
} else if (is_type_polymorphic(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
defer (gb_string_free(str));
|
||||
error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name));
|
||||
|
||||
@@ -4260,11 +4260,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) {
|
||||
return false;
|
||||
}
|
||||
if (o.type == nullptr || o.type == t_invalid) {
|
||||
error(o.expr, "Invalid argument to 'type_of'");
|
||||
return false;
|
||||
}
|
||||
if (o.type == nullptr || o.type == t_invalid) {
|
||||
if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) {
|
||||
error(o.expr, "Invalid argument to 'type_of'");
|
||||
return false;
|
||||
}
|
||||
@@ -4300,7 +4296,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
return false;
|
||||
}
|
||||
Type *t = o.type;
|
||||
if (t == nullptr || t == t_invalid || is_type_polymorphic(t)) {
|
||||
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) {
|
||||
if (is_type_polymorphic(t)) {
|
||||
error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type");
|
||||
} else {
|
||||
@@ -4339,7 +4335,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
return false;
|
||||
}
|
||||
Type *t = o.type;
|
||||
if (t == nullptr || t == t_invalid || is_type_polymorphic(operand->type)) {
|
||||
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) {
|
||||
error(ce->args[0], "Invalid argument for 'typeid_of'");
|
||||
return false;
|
||||
}
|
||||
@@ -10010,6 +10006,57 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ia, InlineAsmExpr, node);
|
||||
if (c->curr_proc_decl == nullptr) {
|
||||
error(node, "Inline asm expressions are only allowed within a procedure body");
|
||||
}
|
||||
|
||||
if (!build_context.use_llvm_api) {
|
||||
error(node, "Inline asm expressions are only currently allowed with -llvm-api");
|
||||
}
|
||||
|
||||
auto param_types = array_make<Type *>(heap_allocator(), ia->param_types.count);
|
||||
Type *return_type = nullptr;
|
||||
for_array(i, ia->param_types) {
|
||||
param_types[i] = check_type(c, ia->param_types[i]);
|
||||
}
|
||||
if (ia->return_type != nullptr) {
|
||||
return_type = check_type(c, ia->return_type);
|
||||
}
|
||||
Operand x = {};
|
||||
check_expr(c, &x, ia->asm_string);
|
||||
if (x.mode != Addressing_Constant || !is_type_string(x.type)) {
|
||||
error(x.expr, "Expected a constant string for the inline asm main parameter");
|
||||
}
|
||||
check_expr(c, &x, ia->constraints_string);
|
||||
if (x.mode != Addressing_Constant || !is_type_string(x.type)) {
|
||||
error(x.expr, "Expected a constant string for the inline asm constraints parameter");
|
||||
}
|
||||
|
||||
Scope *scope = create_scope(c->scope, heap_allocator());
|
||||
scope->flags |= ScopeFlag_Proc;
|
||||
|
||||
Type *params = alloc_type_tuple();
|
||||
Type *results = alloc_type_tuple();
|
||||
if (param_types.count != 0) {
|
||||
array_init(¶ms->Tuple.variables, heap_allocator(), param_types.count);
|
||||
for_array(i, param_types) {
|
||||
params->Tuple.variables[i] = alloc_entity_param(scope, blank_token, param_types[i], false, true);
|
||||
}
|
||||
}
|
||||
if (return_type != nullptr) {
|
||||
array_init(&results->Tuple.variables, heap_allocator(), 1);
|
||||
results->Tuple.variables[0] = alloc_entity_param(scope, blank_token, return_type, false, true);
|
||||
}
|
||||
|
||||
|
||||
Type *pt = alloc_type_proc(scope, params, param_types.count, results, return_type != nullptr ? 1 : 0, false, ProcCC_InlineAsm);
|
||||
o->type = pt;
|
||||
o->mode = Addressing_Value;
|
||||
o->expr = node;
|
||||
return Expr_Expr;
|
||||
case_end;
|
||||
|
||||
case Ast_TypeidType:
|
||||
case Ast_PolyType:
|
||||
case Ast_ProcType:
|
||||
@@ -10539,6 +10586,37 @@ gbString write_expr_to_string(gbString str, Ast *node) {
|
||||
str = gb_string_appendc(str, "" );
|
||||
str = write_expr_to_string(str, rt->type);
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(ia, InlineAsmExpr, node);
|
||||
str = gb_string_appendc(str, "asm(");
|
||||
for_array(i, ia->param_types) {
|
||||
if (i > 0) {
|
||||
str = gb_string_appendc(str, ", ");
|
||||
}
|
||||
str = write_expr_to_string(str, ia->param_types[i]);
|
||||
}
|
||||
str = gb_string_appendc(str, ")");
|
||||
if (ia->return_type != nullptr) {
|
||||
str = gb_string_appendc(str, " -> ");
|
||||
str = write_expr_to_string(str, ia->return_type);
|
||||
}
|
||||
if (ia->has_side_effects) {
|
||||
str = gb_string_appendc(str, " #side_effects");
|
||||
}
|
||||
if (ia->is_align_stack) {
|
||||
str = gb_string_appendc(str, " #stack_align");
|
||||
}
|
||||
if (ia->dialect) {
|
||||
str = gb_string_appendc(str, " #");
|
||||
str = gb_string_appendc(str, inline_asm_dialect_strings[ia->dialect]);
|
||||
}
|
||||
str = gb_string_appendc(str, " {");
|
||||
str = write_expr_to_string(str, ia->asm_string);
|
||||
str = gb_string_appendc(str, ", ");
|
||||
str = write_expr_to_string(str, ia->constraints_string);
|
||||
str = gb_string_appendc(str, "}");
|
||||
case_end;
|
||||
}
|
||||
|
||||
return str;
|
||||
|
||||
@@ -2209,7 +2209,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall
|
||||
return new_type;
|
||||
}
|
||||
|
||||
if (cc == ProcCC_None || cc == ProcCC_PureNone) {
|
||||
if (cc == ProcCC_None || cc == ProcCC_PureNone || cc == ProcCC_InlineAsm) {
|
||||
return new_type;
|
||||
}
|
||||
|
||||
@@ -2328,10 +2328,15 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal
|
||||
|
||||
Type *single_type = reduce_tuple_to_single_type(original_type);
|
||||
|
||||
if (cc == ProcCC_InlineAsm) {
|
||||
return new_type;
|
||||
}
|
||||
|
||||
if (is_type_simd_vector(single_type)) {
|
||||
return new_type;
|
||||
}
|
||||
|
||||
|
||||
if (build_context.ODIN_OS == "windows") {
|
||||
if (build_context.ODIN_ARCH == "amd64") {
|
||||
if (is_type_integer_128bit(single_type)) {
|
||||
@@ -2401,7 +2406,7 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type
|
||||
if (abi_return_type == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (cc == ProcCC_None || cc == ProcCC_PureNone) {
|
||||
if (cc == ProcCC_None || cc == ProcCC_PureNone || cc == ProcCC_InlineAsm) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -9563,6 +9563,43 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
|
||||
case_ast_node(ie, IndexExpr, expr);
|
||||
return lb_addr_load(p, lb_build_addr(p, expr));
|
||||
case_end;
|
||||
|
||||
case_ast_node(ia, InlineAsmExpr, expr);
|
||||
Type *t = type_of_expr(expr);
|
||||
GB_ASSERT(is_type_asm_proc(t));
|
||||
|
||||
String asm_string = {};
|
||||
String constraints_string = {};
|
||||
|
||||
TypeAndValue tav;
|
||||
tav = type_and_value_of_expr(ia->asm_string);
|
||||
GB_ASSERT(is_type_string(tav.type));
|
||||
GB_ASSERT(tav.value.kind == ExactValue_String);
|
||||
asm_string = tav.value.value_string;
|
||||
|
||||
tav = type_and_value_of_expr(ia->constraints_string);
|
||||
GB_ASSERT(is_type_string(tav.type));
|
||||
GB_ASSERT(tav.value.kind == ExactValue_String);
|
||||
constraints_string = tav.value.value_string;
|
||||
|
||||
|
||||
LLVMInlineAsmDialect dialect = LLVMInlineAsmDialectATT;
|
||||
switch (ia->dialect) {
|
||||
case InlineAsmDialect_Default: dialect = LLVMInlineAsmDialectATT; break;
|
||||
case InlineAsmDialect_ATT: dialect = LLVMInlineAsmDialectATT; break;
|
||||
case InlineAsmDialect_Intel: dialect = LLVMInlineAsmDialectIntel; break;
|
||||
default: GB_PANIC("Unhandled inline asm dialect"); break;
|
||||
}
|
||||
|
||||
LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, t));
|
||||
LLVMValueRef the_asm = LLVMGetInlineAsm(func_type,
|
||||
cast(char *)asm_string.text, cast(size_t)asm_string.len,
|
||||
cast(char *)constraints_string.text, cast(size_t)constraints_string.len,
|
||||
ia->has_side_effects, ia->is_align_stack, dialect
|
||||
);
|
||||
GB_ASSERT(the_asm != nullptr);
|
||||
return {the_asm, t};
|
||||
case_end;
|
||||
}
|
||||
|
||||
GB_PANIC("lb_build_expr: %.*s", LIT(ast_strings[expr->kind]));
|
||||
|
||||
105
src/parser.cpp
105
src/parser.cpp
@@ -45,6 +45,7 @@ Token ast_token(Ast *node) {
|
||||
case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr);
|
||||
case Ast_TypeCast: return node->TypeCast.token;
|
||||
case Ast_AutoCast: return node->AutoCast.token;
|
||||
case Ast_InlineAsmExpr: return node->InlineAsmExpr.token;
|
||||
|
||||
case Ast_BadStmt: return node->BadStmt.begin;
|
||||
case Ast_EmptyStmt: return node->EmptyStmt.token;
|
||||
@@ -232,6 +233,13 @@ Ast *clone_ast(Ast *node) {
|
||||
n->AutoCast.expr = clone_ast(n->AutoCast.expr);
|
||||
break;
|
||||
|
||||
case Ast_InlineAsmExpr:
|
||||
n->InlineAsmExpr.param_types = clone_ast_array(n->InlineAsmExpr.param_types);
|
||||
n->InlineAsmExpr.return_type = clone_ast(n->InlineAsmExpr.return_type);
|
||||
n->InlineAsmExpr.asm_string = clone_ast(n->InlineAsmExpr.asm_string);
|
||||
n->InlineAsmExpr.constraints_string = clone_ast(n->InlineAsmExpr.constraints_string);
|
||||
break;
|
||||
|
||||
case Ast_BadStmt: break;
|
||||
case Ast_EmptyStmt: break;
|
||||
case Ast_ExprStmt:
|
||||
@@ -715,6 +723,28 @@ Ast *ast_auto_cast(AstFile *f, Token token, Ast *expr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Ast *ast_inline_asm_expr(AstFile *f, Token token, Token open, Token close,
|
||||
Array<Ast *> const ¶m_types,
|
||||
Ast *return_type,
|
||||
Ast *asm_string,
|
||||
Ast *constraints_string,
|
||||
bool has_side_effects,
|
||||
bool is_align_stack,
|
||||
InlineAsmDialectKind dialect) {
|
||||
|
||||
Ast *result = alloc_ast_node(f, Ast_InlineAsmExpr);
|
||||
result->InlineAsmExpr.token = token;
|
||||
result->InlineAsmExpr.open = open;
|
||||
result->InlineAsmExpr.close = close;
|
||||
result->InlineAsmExpr.param_types = param_types;
|
||||
result->InlineAsmExpr.return_type = return_type;
|
||||
result->InlineAsmExpr.asm_string = asm_string;
|
||||
result->InlineAsmExpr.constraints_string = constraints_string;
|
||||
result->InlineAsmExpr.has_side_effects = has_side_effects;
|
||||
result->InlineAsmExpr.is_align_stack = is_align_stack;
|
||||
result->InlineAsmExpr.dialect = dialect;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2316,6 +2346,80 @@ Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
return ast_bit_set_type(f, token, elem, underlying);
|
||||
}
|
||||
|
||||
case Token_asm: {
|
||||
Token token = expect_token(f, Token_asm);
|
||||
|
||||
Array<Ast *> param_types = {};
|
||||
Ast *return_type = nullptr;
|
||||
if (allow_token(f, Token_OpenParen)) {
|
||||
param_types = array_make<Ast *>(heap_allocator());
|
||||
while (f->curr_token.kind != Token_CloseParen && f->curr_token.kind != Token_EOF) {
|
||||
Ast *t = parse_type(f);
|
||||
array_add(¶m_types, t);
|
||||
if (f->curr_token.kind != Token_Comma ||
|
||||
f->curr_token.kind == Token_EOF) {
|
||||
break;
|
||||
}
|
||||
advance_token(f);
|
||||
}
|
||||
expect_token(f, Token_CloseParen);
|
||||
|
||||
if (allow_token(f, Token_ArrowRight)) {
|
||||
return_type = parse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
bool has_side_effects = false;
|
||||
bool is_align_stack = false;
|
||||
InlineAsmDialectKind dialect = InlineAsmDialect_Default;
|
||||
|
||||
while (f->curr_token.kind == Token_Hash) {
|
||||
advance_token(f);
|
||||
if (f->curr_token.kind == Token_Ident) {
|
||||
Token token = advance_token(f);
|
||||
String name = token.string;
|
||||
if (name == "side_effects") {
|
||||
if (has_side_effects) {
|
||||
syntax_error(token, "Duplicate directive on inline asm expression: '#side_effects'");
|
||||
}
|
||||
has_side_effects = true;
|
||||
} else if (name == "align_stack") {
|
||||
if (is_align_stack) {
|
||||
syntax_error(token, "Duplicate directive on inline asm expression: '#align_stack'");
|
||||
}
|
||||
is_align_stack = true;
|
||||
} else if (name == "att") {
|
||||
if (dialect == InlineAsmDialect_ATT) {
|
||||
syntax_error(token, "Duplicate directive on inline asm expression: '#att'");
|
||||
} else if (dialect != InlineAsmDialect_Default) {
|
||||
syntax_error(token, "Conflicting asm dialects");
|
||||
} else {
|
||||
dialect = InlineAsmDialect_ATT;
|
||||
}
|
||||
} else if (name == "intel") {
|
||||
if (dialect == InlineAsmDialect_Intel) {
|
||||
syntax_error(token, "Duplicate directive on inline asm expression: '#intel'");
|
||||
} else if (dialect != InlineAsmDialect_Default) {
|
||||
syntax_error(token, "Conflicting asm dialects");
|
||||
} else {
|
||||
dialect = InlineAsmDialect_Intel;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
syntax_error(f->curr_token, "Expected an identifier after hash");
|
||||
}
|
||||
}
|
||||
|
||||
Token open = expect_token(f, Token_OpenBrace);
|
||||
Ast *asm_string = parse_expr(f, false);
|
||||
expect_token(f, Token_Comma);
|
||||
Ast *constraints_string = parse_expr(f, false);
|
||||
allow_token(f, Token_Comma);
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return ast_inline_asm_expr(f, token, open, close, param_types, return_type, asm_string, constraints_string, has_side_effects, is_align_stack, dialect);
|
||||
}
|
||||
|
||||
default: {
|
||||
#if 0
|
||||
Ast *type = parse_type_or_ident(f);
|
||||
@@ -4142,6 +4246,7 @@ Ast *parse_stmt(AstFile *f) {
|
||||
case Token_String:
|
||||
case Token_OpenParen:
|
||||
case Token_Pointer:
|
||||
case Token_asm: // Inline assembly
|
||||
// Unary Operators
|
||||
case Token_Add:
|
||||
case Token_Sub:
|
||||
|
||||
@@ -209,6 +209,8 @@ enum ProcCallingConvention {
|
||||
ProcCC_None = 7,
|
||||
ProcCC_PureNone = 8,
|
||||
|
||||
ProcCC_InlineAsm = 9,
|
||||
|
||||
ProcCC_MAX,
|
||||
|
||||
|
||||
@@ -250,6 +252,20 @@ enum StmtAllowFlag {
|
||||
StmtAllowFlag_Label = 1<<1,
|
||||
};
|
||||
|
||||
enum InlineAsmDialectKind : u8 {
|
||||
InlineAsmDialect_Default, // ATT is default
|
||||
InlineAsmDialect_ATT,
|
||||
InlineAsmDialect_Intel,
|
||||
|
||||
InlineAsmDialect_COUNT,
|
||||
};
|
||||
|
||||
char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
|
||||
"",
|
||||
"att",
|
||||
"intel",
|
||||
};
|
||||
|
||||
#define AST_KINDS \
|
||||
AST_KIND(Ident, "identifier", struct { \
|
||||
Token token; \
|
||||
@@ -323,6 +339,17 @@ AST_KIND(_ExprBegin, "", bool) \
|
||||
AST_KIND(TypeAssertion, "type assertion", struct { Ast *expr; Token dot; Ast *type; Type *type_hint; }) \
|
||||
AST_KIND(TypeCast, "type cast", struct { Token token; Ast *type, *expr; }) \
|
||||
AST_KIND(AutoCast, "auto_cast", struct { Token token; Ast *expr; }) \
|
||||
AST_KIND(InlineAsmExpr, "inline asm expression", struct { \
|
||||
Token token; \
|
||||
Token open, close; \
|
||||
Array<Ast *> param_types; \
|
||||
Ast *return_type; \
|
||||
Ast *asm_string; \
|
||||
Ast *constraints_string; \
|
||||
bool has_side_effects; \
|
||||
bool is_align_stack; \
|
||||
InlineAsmDialectKind dialect; \
|
||||
}) \
|
||||
AST_KIND(_ExprEnd, "", bool) \
|
||||
AST_KIND(_StmtBegin, "", bool) \
|
||||
AST_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
||||
@@ -337,10 +364,6 @@ AST_KIND(_StmtBegin, "", bool) \
|
||||
Token op; \
|
||||
Array<Ast *> lhs, rhs; \
|
||||
}) \
|
||||
AST_KIND(IncDecStmt, "increment decrement statement", struct { \
|
||||
Token op; \
|
||||
Ast *expr; \
|
||||
}) \
|
||||
AST_KIND(_ComplexStmtBegin, "", bool) \
|
||||
AST_KIND(BlockStmt, "block statement", struct { \
|
||||
Array<Ast *> stmts; \
|
||||
|
||||
@@ -117,6 +117,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
|
||||
TOKEN_KIND(Token_inline, "inline"), \
|
||||
TOKEN_KIND(Token_no_inline, "no_inline"), \
|
||||
TOKEN_KIND(Token_context, "context"), \
|
||||
TOKEN_KIND(Token_asm, "asm"), \
|
||||
TOKEN_KIND(Token_macro, "macro"), \
|
||||
TOKEN_KIND(Token_const, "const"), \
|
||||
TOKEN_KIND(Token__KeywordEnd, ""), \
|
||||
|
||||
@@ -1210,6 +1210,10 @@ bool is_type_proc(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Proc;
|
||||
}
|
||||
bool is_type_asm_proc(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Proc && t->Proc.calling_convention == ProcCC_InlineAsm;
|
||||
}
|
||||
bool is_type_poly_proc(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Proc && t->Proc.is_polymorphic;
|
||||
|
||||
Reference in New Issue
Block a user