aboutsummaryrefslogtreecommitdiff
path: root/src/ir.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ir.cpp')
-rw-r--r--src/ir.cpp188
1 files changed, 160 insertions, 28 deletions
diff --git a/src/ir.cpp b/src/ir.cpp
index 076aadb9c8..f8301f0116 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -513,6 +513,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
return IrInstructionIdSliceType;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionGlobalAsm *) {
+ return IrInstructionIdGlobalAsm;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {
return IrInstructionIdAsm;
}
@@ -1628,10 +1632,21 @@ static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode
return &instruction->base;
}
-static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction **input_list,
- IrInstruction **output_types, ZigVar **output_vars, size_t return_count, bool has_side_effects)
+static IrInstruction *ir_build_global_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *asm_code) {
+ IrInstructionGlobalAsm *instruction = ir_build_instruction<IrInstructionGlobalAsm>(irb, scope, source_node);
+ instruction->asm_code = asm_code;
+ return &instruction->base;
+}
+
+static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ Buf *asm_template, AsmToken *token_list, size_t token_list_len,
+ IrInstruction **input_list, IrInstruction **output_types, ZigVar **output_vars, size_t return_count,
+ bool has_side_effects)
{
IrInstructionAsm *instruction = ir_build_instruction<IrInstructionAsm>(irb, scope, source_node);
+ instruction->asm_template = asm_template;
+ instruction->token_list = token_list;
+ instruction->token_list_len = token_list_len;
instruction->input_list = input_list;
instruction->output_types = output_types;
instruction->output_vars = output_vars;
@@ -5861,21 +5876,142 @@ static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, Ast
return ir_build_const_undefined(irb, scope, node);
}
+static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_template,
+ ZigList<AsmToken> *tok_list)
+{
+ // TODO Connect the errors in this function back up to the actual source location
+ // rather than just the token. https://github.com/ziglang/zig/issues/2080
+ enum State {
+ StateStart,
+ StatePercent,
+ StateTemplate,
+ StateVar,
+ };
+
+ assert(tok_list->length == 0);
+
+ AsmToken *cur_tok = nullptr;
+
+ enum State state = StateStart;
+
+ for (size_t i = 0; i < buf_len(asm_template); i += 1) {
+ uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
+ switch (state) {
+ case StateStart:
+ if (c == '%') {
+ tok_list->add_one();
+ cur_tok = &tok_list->last();
+ cur_tok->id = AsmTokenIdPercent;
+ cur_tok->start = i;
+ state = StatePercent;
+ } else {
+ tok_list->add_one();
+ cur_tok = &tok_list->last();
+ cur_tok->id = AsmTokenIdTemplate;
+ cur_tok->start = i;
+ state = StateTemplate;
+ }
+ break;
+ case StatePercent:
+ if (c == '%') {
+ cur_tok->end = i;
+ state = StateStart;
+ } else if (c == '[') {
+ cur_tok->id = AsmTokenIdVar;
+ state = StateVar;
+ } else if (c == '=') {
+ cur_tok->id = AsmTokenIdUniqueId;
+ cur_tok->end = i;
+ state = StateStart;
+ } else {
+ add_node_error(irb->codegen, source_node,
+ buf_create_from_str("expected a '%' or '['"));
+ return ErrorSemanticAnalyzeFail;
+ }
+ break;
+ case StateTemplate:
+ if (c == '%') {
+ cur_tok->end = i;
+ i -= 1;
+ cur_tok = nullptr;
+ state = StateStart;
+ }
+ break;
+ case StateVar:
+ if (c == ']') {
+ cur_tok->end = i;
+ state = StateStart;
+ } else if ((c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ (c == '_'))
+ {
+ // do nothing
+ } else {
+ add_node_error(irb->codegen, source_node,
+ buf_sprintf("invalid substitution character: '%c'", c));
+ return ErrorSemanticAnalyzeFail;
+ }
+ break;
+ }
+ }
+
+ switch (state) {
+ case StateStart:
+ break;
+ case StatePercent:
+ case StateVar:
+ add_node_error(irb->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
+ return ErrorSemanticAnalyzeFail;
+ case StateTemplate:
+ cur_tok->end = buf_len(asm_template);
+ break;
+ }
+ return ErrorNone;
+}
+
static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
+ Error err;
assert(node->type == NodeTypeAsmExpr);
+ AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
+ bool is_volatile = asm_expr->volatile_token != nullptr;
+ bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
+
+ Buf *template_buf = &asm_expr->asm_template->data.str_lit.str;
- IrInstruction **input_list = allocate<IrInstruction *>(node->data.asm_expr.input_list.length);
- IrInstruction **output_types = allocate<IrInstruction *>(node->data.asm_expr.output_list.length);
- ZigVar **output_vars = allocate<ZigVar *>(node->data.asm_expr.output_list.length);
+ if (!in_fn_scope) {
+ if (is_volatile) {
+ add_token_error(irb->codegen, node->owner, asm_expr->volatile_token,
+ buf_sprintf("volatile is meaningless on global assembly"));
+ return irb->codegen->invalid_instruction;
+ }
+
+ if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
+ asm_expr->clobber_list.length != 0)
+ {
+ add_node_error(irb->codegen, node,
+ buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
+ return irb->codegen->invalid_instruction;
+ }
+
+ return ir_build_global_asm(irb, scope, node, template_buf);
+ }
+
+ ZigList<AsmToken> tok_list = {};
+ if ((err = parse_asm_template(irb, node, template_buf, &tok_list))) {
+ return irb->codegen->invalid_instruction;
+ }
+
+ IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
+ IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
+ ZigVar **output_vars = allocate<ZigVar *>(asm_expr->output_list.length);
size_t return_count = 0;
- bool is_volatile = node->data.asm_expr.is_volatile;
- if (!is_volatile && node->data.asm_expr.output_list.length == 0) {
+ if (!is_volatile && asm_expr->output_list.length == 0) {
add_node_error(irb->codegen, node,
buf_sprintf("assembly expression with no output must be marked volatile"));
return irb->codegen->invalid_instruction;
}
- for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
- AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
+ for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
+ AsmOutput *asm_output = asm_expr->output_list.at(i);
if (asm_output->return_type) {
return_count += 1;
@@ -5911,8 +6047,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
return irb->codegen->invalid_instruction;
}
}
- for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
- AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
+ for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
+ AsmInput *asm_input = asm_expr->input_list.at(i);
IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope);
if (input_value == irb->codegen->invalid_instruction)
return irb->codegen->invalid_instruction;
@@ -5920,7 +6056,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
input_list[i] = input_value;
}
- return ir_build_asm(irb, scope, node, input_list, output_types, output_vars, return_count, is_volatile);
+ return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length,
+ input_list, output_types, output_vars, return_count, is_volatile);
}
static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
@@ -16309,27 +16446,18 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
zig_unreachable();
}
+static IrInstruction *ir_analyze_instruction_global_asm(IrAnalyze *ira, IrInstructionGlobalAsm *instruction) {
+ buf_append_char(&ira->codegen->global_asm, '\n');
+ buf_append_buf(&ira->codegen->global_asm, instruction->asm_code);
+
+ return ir_const_void(ira, &instruction->base);
+}
+
static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
- bool global_scope = (scope_fn_entry(asm_instruction->base.scope) == nullptr);
- if (global_scope) {
- if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
- asm_expr->clobber_list.length != 0)
- {
- ir_add_error(ira, &asm_instruction->base,
- buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
- return ira->codegen->invalid_instruction;
- }
-
- buf_append_char(&ira->codegen->global_asm, '\n');
- buf_append_buf(&ira->codegen->global_asm, asm_expr->asm_template);
-
- return ir_const_void(ira, &asm_instruction->base);
- }
-
if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
return ira->codegen->invalid_instruction;
@@ -16367,6 +16495,7 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs
IrInstruction *result = ir_build_asm(&ira->new_irb,
asm_instruction->base.scope, asm_instruction->base.source_node,
+ asm_instruction->asm_template, asm_instruction->token_list, asm_instruction->token_list_len,
input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count,
asm_instruction->has_side_effects);
result->value.type = return_type;
@@ -22584,6 +22713,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction);
case IrInstructionIdSliceType:
return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
+ case IrInstructionIdGlobalAsm:
+ return ir_analyze_instruction_global_asm(ira, (IrInstructionGlobalAsm *)instruction);
case IrInstructionIdAsm:
return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
case IrInstructionIdArrayType:
@@ -22938,6 +23069,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCmpxchgSrc:
case IrInstructionIdAssertZero:
case IrInstructionIdResizeSlice:
+ case IrInstructionIdGlobalAsm:
return true;
case IrInstructionIdPhi: