aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2020-10-24 16:32:37 +0100
committergingerBill <bill@gingerbill.org>2020-10-24 16:32:37 +0100
commit4629754f7ced5df477eb017872ef65539db64a27 (patch)
treefc2fc4b7a493c58adeb10bf54254fbbad66f2f67
parent0061e63db010cdb2043af63527b4c25f45652a51 (diff)
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 {...}`
-rw-r--r--src/check_decl.cpp6
-rw-r--r--src/check_expr.cpp92
-rw-r--r--src/check_type.cpp9
-rw-r--r--src/llvm_backend.cpp37
-rw-r--r--src/parser.cpp105
-rw-r--r--src/parser.hpp31
-rw-r--r--src/tokenizer.cpp1
-rw-r--r--src/types.cpp4
8 files changed, 271 insertions, 14 deletions
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 639bf7d4f..1aafa6e1c 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -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));
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index fe43a12a6..02b54c80a 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -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(&params->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;
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 4d1359848..93040e493 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -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;
}
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 65586d3a6..4abc65ab4 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -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]));
diff --git a/src/parser.cpp b/src/parser.cpp
index de5c48d20..330d78263 100644
--- a/src/parser.cpp
+++ b/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 &param_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(&param_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:
diff --git a/src/parser.hpp b/src/parser.hpp
index ff47d1887..8e210876f 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -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; \
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 947e3e7a0..d89ec43b5 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -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, ""), \
diff --git a/src/types.cpp b/src/types.cpp
index 03e071045..fc4544385 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -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;