Commit 26c8930b95

LemonBoy <thatlemon@gmail.com>
2019-12-02 21:07:44
Accept comptime-known expression for asm
1 parent b7be577
lib/std/zig/parse.zig
@@ -1495,13 +1495,13 @@ fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
     return &node.base;
 }
 
-/// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN
+/// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
 fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
     const asm_token = eatToken(it, .Keyword_asm) orelse return null;
     const volatile_token = eatToken(it, .Keyword_volatile);
     _ = try expectToken(it, tree, .LParen);
-    const template = try expectNode(arena, it, tree, parseStringLiteral, AstError{
-        .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index },
+    const template = try expectNode(arena, it, tree, parseExpr, AstError{
+        .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
     });
 
     const node = try arena.create(Node.Asm);
lib/std/zig/parser_test.zig
@@ -1,3 +1,26 @@
+test "zig fmt: asm expression with comptime content" {
+    try testCanonical(
+        \\comptime {
+        \\    asm ("foo" ++ "bar");
+        \\}
+        \\pub fn main() void {
+        \\    asm volatile ("foo" ++ "bar");
+        \\    asm volatile ("foo" ++ "bar"
+        \\        : [_] "" (x)
+        \\    );
+        \\    asm volatile ("foo" ++ "bar"
+        \\        : [_] "" (x)
+        \\        : [_] "" (y)
+        \\    );
+        \\    asm volatile ("foo" ++ "bar"
+        \\        : [_] "" (x)
+        \\        : [_] "" (y)
+        \\        : "h", "e", "l", "l", "o"
+        \\    );
+        \\}
+        \\
+    );
+}
 test "zig fmt: var struct field" {
     try testCanonical(
         \\pub const Pointer = struct {
src/all_types.hpp
@@ -949,7 +949,7 @@ struct AsmToken {
 
 struct AstNodeAsmExpr {
     Token *volatile_token;
-    Token *asm_template;
+    AstNode *asm_template;
     ZigList<AsmOutput*> output_list;
     ZigList<AsmInput*> input_list;
     ZigList<Buf*> clobber_list;
@@ -2496,8 +2496,8 @@ enum IrInstructionId {
     IrInstructionIdArrayType,
     IrInstructionIdAnyFrameType,
     IrInstructionIdSliceType,
-    IrInstructionIdGlobalAsm,
-    IrInstructionIdAsm,
+    IrInstructionIdAsmSrc,
+    IrInstructionIdAsmGen,
     IrInstructionIdSizeOf,
     IrInstructionIdTestNonNull,
     IrInstructionIdOptionalUnwrapPtr,
@@ -3049,13 +3049,19 @@ struct IrInstructionSliceType {
     bool is_allow_zero;
 };
 
-struct IrInstructionGlobalAsm {
+struct IrInstructionAsmSrc {
     IrInstruction base;
 
-    Buf *asm_code;
+    IrInstruction *asm_template;
+    IrInstruction **input_list;
+    IrInstruction **output_types;
+    ZigVar **output_vars;
+    size_t return_count;
+    bool has_side_effects;
+    bool is_global;
 };
 
-struct IrInstructionAsm {
+struct IrInstructionAsmGen {
     IrInstruction base;
 
     Buf *asm_template;
src/ast_render.cpp
@@ -884,7 +884,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
             {
                 AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
                 const char *volatile_str = (asm_expr->volatile_token != nullptr) ? " volatile" : "";
-                fprintf(ar->f, "asm%s (\"%s\"\n", volatile_str, buf_ptr(&asm_expr->asm_template->data.str_lit.str));
+                fprintf(ar->f, "asm%s (", volatile_str);
+                render_node_ungrouped(ar, asm_expr->asm_template);
+                fprintf(ar->f, ")");
                 print_indent(ar);
                 fprintf(ar->f, ": ");
                 for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
src/codegen.cpp
@@ -4437,7 +4437,7 @@ static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_
     return SIZE_MAX;
 }
 
-static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstructionAsm *instruction) {
+static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutable *executable, IrInstructionAsmGen *instruction) {
     AstNode *asm_node = instruction->base.source_node;
     assert(asm_node->type == NodeTypeAsmExpr);
     AstNodeAsmExpr *asm_expr = &asm_node->data.asm_expr;
@@ -6135,7 +6135,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdPtrCastSrc:
         case IrInstructionIdCmpxchgSrc:
         case IrInstructionIdLoadPtr:
-        case IrInstructionIdGlobalAsm:
         case IrInstructionIdHasDecl:
         case IrInstructionIdUndeclaredIdent:
         case IrInstructionIdCallSrc:
@@ -6156,6 +6155,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdAwaitSrc:
         case IrInstructionIdSplatSrc:
         case IrInstructionIdMergeErrSets:
+        case IrInstructionIdAsmSrc:
             zig_unreachable();
 
         case IrInstructionIdDeclVarGen:
@@ -6192,8 +6192,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
         case IrInstructionIdUnionFieldPtr:
             return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction);
-        case IrInstructionIdAsm:
-            return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
+        case IrInstructionIdAsmGen:
+            return ir_render_asm_gen(g, executable, (IrInstructionAsmGen *)instruction);
         case IrInstructionIdTestNonNull:
             return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction);
         case IrInstructionIdOptionalUnwrapPtr:
src/ir.cpp
@@ -339,10 +339,10 @@ static void destroy_instruction(IrInstruction *inst) {
             return destroy(reinterpret_cast<IrInstructionSliceType *>(inst), name);
         case IrInstructionIdAnyFrameType:
             return destroy(reinterpret_cast<IrInstructionAnyFrameType *>(inst), name);
-        case IrInstructionIdGlobalAsm:
-            return destroy(reinterpret_cast<IrInstructionGlobalAsm *>(inst), name);
-        case IrInstructionIdAsm:
-            return destroy(reinterpret_cast<IrInstructionAsm *>(inst), name);
+        case IrInstructionIdAsmSrc:
+            return destroy(reinterpret_cast<IrInstructionAsmSrc *>(inst), name);
+        case IrInstructionIdAsmGen:
+            return destroy(reinterpret_cast<IrInstructionAsmGen *>(inst), name);
         case IrInstructionIdSizeOf:
             return destroy(reinterpret_cast<IrInstructionSizeOf *>(inst), name);
         case IrInstructionIdTestNonNull:
@@ -1028,12 +1028,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
     return IrInstructionIdSliceType;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionGlobalAsm *) {
-    return IrInstructionIdGlobalAsm;
+static constexpr IrInstructionId ir_instruction_id(IrInstructionAsmSrc *) {
+    return IrInstructionIdAsmSrc;
 }
 
-static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {
-    return IrInstructionIdAsm;
+static constexpr IrInstructionId ir_instruction_id(IrInstructionAsmGen *) {
+    return IrInstructionIdAsmGen;
 }
 
 static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) {
@@ -2268,18 +2268,39 @@ static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
-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;
+static IrInstruction *ir_build_asm_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *asm_template, IrInstruction **input_list, IrInstruction **output_types,
+        ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global)
+{
+    IrInstructionAsmSrc *instruction = ir_build_instruction<IrInstructionAsmSrc>(irb, scope, source_node);
+    instruction->asm_template = asm_template;
+    instruction->input_list = input_list;
+    instruction->output_types = output_types;
+    instruction->output_vars = output_vars;
+    instruction->return_count = return_count;
+    instruction->has_side_effects = has_side_effects;
+    instruction->is_global = is_global;
+
+    assert(source_node->type == NodeTypeAsmExpr);
+    for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
+        IrInstruction *output_type = output_types[i];
+        if (output_type) ir_ref_instruction(output_type, irb->current_basic_block);
+    }
+
+    for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) {
+        IrInstruction *input_value = input_list[i];
+        ir_ref_instruction(input_value, irb->current_basic_block);
+    }
+
     return &instruction->base;
 }
 
-static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node,
+static IrInstruction *ir_build_asm_gen(IrAnalyze *ira, 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);
+    IrInstructionAsmGen *instruction = ir_build_instruction<IrInstructionAsmGen>(&ira->new_irb, scope, source_node);
     instruction->asm_template = asm_template;
     instruction->token_list = token_list;
     instruction->token_list_len = token_list_len;
@@ -2292,12 +2313,12 @@ static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source
     assert(source_node->type == NodeTypeAsmExpr);
     for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
         IrInstruction *output_type = output_types[i];
-        if (output_type) ir_ref_instruction(output_type, irb->current_basic_block);
+        if (output_type) ir_ref_instruction(output_type, ira->new_irb.current_basic_block);
     }
 
     for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) {
         IrInstruction *input_value = input_list[i];
-        ir_ref_instruction(input_value, irb->current_basic_block);
+        ir_ref_instruction(input_value, ira->new_irb.current_basic_block);
     }
 
     return &instruction->base;
@@ -7494,7 +7515,7 @@ 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,
+static Error parse_asm_template(IrAnalyze *ira, AstNode *source_node, Buf *asm_template,
         ZigList<AsmToken> *tok_list)
 {
     // TODO Connect the errors in this function back up to the actual source location
@@ -7542,7 +7563,7 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t
                     cur_tok->end = i;
                     state = StateStart;
                 } else {
-                    add_node_error(irb->codegen, source_node,
+                    add_node_error(ira->codegen, source_node,
                         buf_create_from_str("expected a '%' or '['"));
                     return ErrorSemanticAnalyzeFail;
                 }
@@ -7565,7 +7586,7 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t
                 {
                     // do nothing
                 } else {
-                    add_node_error(irb->codegen, source_node,
+                    add_node_error(ira->codegen, source_node,
                         buf_sprintf("invalid substitution character: '%c'", c));
                     return ErrorSemanticAnalyzeFail;
                 }
@@ -7578,7 +7599,7 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t
             break;
         case StatePercent:
         case StateVar:
-            add_node_error(irb->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
+            add_node_error(ira->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
             return ErrorSemanticAnalyzeFail;
         case StateTemplate:
             cur_tok->end = buf_len(asm_template);
@@ -7607,14 +7628,16 @@ static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_
 }
 
 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;
+
+    IrInstruction *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope);
+    if (asm_template == irb->codegen->invalid_instruction)
+        return irb->codegen->invalid_instruction;
+
     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;
-
     if (!in_fn_scope) {
         if (is_volatile) {
             add_token_error(irb->codegen, node->owner, asm_expr->volatile_token,
@@ -7630,12 +7653,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
             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;
+        return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr,
+                                nullptr, 0, is_volatile, true);
     }
 
     IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
@@ -7693,24 +7712,8 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
         input_list[i] = input_value;
     }
 
-    for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) {
-        AsmToken asm_token = tok_list.at(token_i);
-        if (asm_token.id == AsmTokenIdVar) {
-            size_t index = find_asm_index(irb->codegen, node, &asm_token, template_buf);
-            if (index == SIZE_MAX) {
-                const char *ptr = buf_ptr(template_buf) + asm_token.start + 2;
-                uint32_t len = asm_token.end - asm_token.start - 2;
-
-                add_node_error(irb->codegen, node,
-                    buf_sprintf("could not find '%.*s' in the inputs or outputs",
-                        len, ptr));
-                return irb->codegen->invalid_instruction;
-            }
-        }
-    }
-
-    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);
+    return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types,
+                            output_vars, return_count, is_volatile, false);
 }
 
 static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
@@ -20150,21 +20153,49 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
     return result;
 }
 
-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, IrInstructionAsmSrc *asm_instruction) {
+    Error err;
 
-static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
     assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
 
+    AstNode *node = asm_instruction->base.source_node;
     AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
 
+    Buf *template_buf = ir_resolve_str(ira, asm_instruction->asm_template->child);
+    if (template_buf == nullptr)
+        return ira->codegen->invalid_instruction;
+
+    if (asm_instruction->is_global) {
+        buf_append_char(&ira->codegen->global_asm, '\n');
+        buf_append_buf(&ira->codegen->global_asm, template_buf);
+
+        return ir_const_void(ira, &asm_instruction->base);
+    }
+
     if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
         return ira->codegen->invalid_instruction;
 
+    ZigList<AsmToken> tok_list = {};
+    if ((err = parse_asm_template(ira, node, template_buf, &tok_list))) {
+        return ira->codegen->invalid_instruction;
+    }
+
+    for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) {
+        AsmToken asm_token = tok_list.at(token_i);
+        if (asm_token.id == AsmTokenIdVar) {
+            size_t index = find_asm_index(ira->codegen, node, &asm_token, template_buf);
+            if (index == SIZE_MAX) {
+                const char *ptr = buf_ptr(template_buf) + asm_token.start + 2;
+                uint32_t len = asm_token.end - asm_token.start - 2;
+
+                add_node_error(ira->codegen, node,
+                    buf_sprintf("could not find '%.*s' in the inputs or outputs",
+                        len, ptr));
+                return ira->codegen->invalid_instruction;
+            }
+        }
+    }
+
     // TODO validate the output types and variable types
 
     IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
@@ -20197,9 +20228,9 @@ static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAs
         input_list[i] = input_value;
     }
 
-    IrInstruction *result = ir_build_asm(&ira->new_irb,
+    IrInstruction *result = ir_build_asm_gen(ira,
         asm_instruction->base.scope, asm_instruction->base.source_node,
-        asm_instruction->asm_template, asm_instruction->token_list, asm_instruction->token_list_len,
+        template_buf, tok_list.items, tok_list.length,
         input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count,
         asm_instruction->has_side_effects);
     result->value->type = return_type;
@@ -27722,6 +27753,7 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
         case IrInstructionIdSplatGen:
         case IrInstructionIdVectorExtractElem:
         case IrInstructionIdVectorStoreElem:
+        case IrInstructionIdAsmGen:
             zig_unreachable();
 
         case IrInstructionIdReturn:
@@ -27768,10 +27800,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
             return ir_analyze_instruction_any_frame_type(ira, (IrInstructionAnyFrameType *)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 IrInstructionIdAsmSrc:
+            return ir_analyze_instruction_asm(ira, (IrInstructionAsmSrc *)instruction);
         case IrInstructionIdArrayType:
             return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
         case IrInstructionIdSizeOf:
@@ -28183,7 +28213,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdAssertZero:
         case IrInstructionIdAssertNonNull:
         case IrInstructionIdResizeSlice:
-        case IrInstructionIdGlobalAsm:
         case IrInstructionIdUndeclaredIdent:
         case IrInstructionIdEndExpr:
         case IrInstructionIdPtrOfArrayToSlice:
@@ -28303,9 +28332,15 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdVectorExtractElem:
             return false;
 
-        case IrInstructionIdAsm:
+        case IrInstructionIdAsmSrc:
+            {
+                IrInstructionAsmSrc *asm_instruction = (IrInstructionAsmSrc *)instruction;
+                return asm_instruction->has_side_effects;
+            }
+
+        case IrInstructionIdAsmGen:
             {
-                IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction;
+                IrInstructionAsmGen *asm_instruction = (IrInstructionAsmGen *)instruction;
                 return asm_instruction->has_side_effects;
             }
         case IrInstructionIdUnwrapErrPayload:
src/ir_print.cpp
@@ -124,10 +124,10 @@ const char* ir_instruction_type_str(IrInstructionId id) {
             return "AnyFrameType";
         case IrInstructionIdSliceType:
             return "SliceType";
-        case IrInstructionIdGlobalAsm:
-            return "GlobalAsm";
-        case IrInstructionIdAsm:
-            return "Asm";
+        case IrInstructionIdAsmSrc:
+            return "AsmSrc";
+        case IrInstructionIdAsmGen:
+            return "AsmGen";
         case IrInstructionIdSizeOf:
             return "SizeOf";
         case IrInstructionIdTestNonNull:
@@ -888,11 +888,50 @@ static void ir_print_any_frame_type(IrPrint *irp, IrInstructionAnyFrameType *ins
     }
 }
 
-static void ir_print_global_asm(IrPrint *irp, IrInstructionGlobalAsm *instruction) {
-    fprintf(irp->f, "asm(\"%s\")", buf_ptr(instruction->asm_code));
+static void ir_print_asm_src(IrPrint *irp, IrInstructionAsmSrc *instruction) {
+    assert(instruction->base.source_node->type == NodeTypeAsmExpr);
+    AstNodeAsmExpr *asm_expr = &instruction->base.source_node->data.asm_expr;
+    const char *volatile_kw = instruction->has_side_effects ? " volatile" : "";
+    fprintf(irp->f, "asm%s (", volatile_kw);
+    ir_print_other_instruction(irp, instruction->asm_template);
+
+    for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
+        AsmOutput *asm_output = asm_expr->output_list.at(i);
+        if (i != 0) fprintf(irp->f, ", ");
+
+        fprintf(irp->f, "[%s] \"%s\" (",
+                buf_ptr(asm_output->asm_symbolic_name),
+                buf_ptr(asm_output->constraint));
+        if (asm_output->return_type) {
+            fprintf(irp->f, "-> ");
+            ir_print_other_instruction(irp, instruction->output_types[i]);
+        } else {
+            fprintf(irp->f, "%s", buf_ptr(asm_output->variable_name));
+        }
+        fprintf(irp->f, ")");
+    }
+
+    fprintf(irp->f, " : ");
+    for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
+        AsmInput *asm_input = asm_expr->input_list.at(i);
+
+        if (i != 0) fprintf(irp->f, ", ");
+        fprintf(irp->f, "[%s] \"%s\" (",
+                buf_ptr(asm_input->asm_symbolic_name),
+                buf_ptr(asm_input->constraint));
+        ir_print_other_instruction(irp, instruction->input_list[i]);
+        fprintf(irp->f, ")");
+    }
+    fprintf(irp->f, " : ");
+    for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1) {
+        Buf *reg_name = asm_expr->clobber_list.at(i);
+        if (i != 0) fprintf(irp->f, ", ");
+        fprintf(irp->f, "\"%s\"", buf_ptr(reg_name));
+    }
+    fprintf(irp->f, ")");
 }
 
-static void ir_print_asm(IrPrint *irp, IrInstructionAsm *instruction) {
+static void ir_print_asm_gen(IrPrint *irp, IrInstructionAsmGen *instruction) {
     assert(instruction->base.source_node->type == NodeTypeAsmExpr);
     AstNodeAsmExpr *asm_expr = &instruction->base.source_node->data.asm_expr;
     const char *volatile_kw = instruction->has_side_effects ? " volatile" : "";
@@ -2121,11 +2160,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool
         case IrInstructionIdAnyFrameType:
             ir_print_any_frame_type(irp, (IrInstructionAnyFrameType *)instruction);
             break;
-        case IrInstructionIdGlobalAsm:
-            ir_print_global_asm(irp, (IrInstructionGlobalAsm *)instruction);
+        case IrInstructionIdAsmSrc:
+            ir_print_asm_src(irp, (IrInstructionAsmSrc *)instruction);
             break;
-        case IrInstructionIdAsm:
-            ir_print_asm(irp, (IrInstructionAsm *)instruction);
+        case IrInstructionIdAsmGen:
+            ir_print_asm_gen(irp, (IrInstructionAsmGen *)instruction);
             break;
         case IrInstructionIdSizeOf:
             ir_print_size_of(irp, (IrInstructionSizeOf *)instruction);
src/parser.cpp
@@ -1891,7 +1891,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) {
 
     Token *volatile_token = eat_token_if(pc, TokenIdKeywordVolatile);
     expect_token(pc, TokenIdLParen);
-    Token *asm_template = expect_token(pc, TokenIdStringLiteral);
+    AstNode *asm_template = ast_expect(pc, ast_parse_expr);
     AstNode *res = ast_parse_asm_output(pc);
     if (res == nullptr)
         res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);