Commit b40a8efb9a

Andrew Kelley <andrew@ziglang.org>
2021-04-24 03:28:46
stage2: implement `anyframe`, `anyframe->T` and fix assembly
* AstGen: implement `anyframe_literal` and `anyframe_type`. * Introduce `makeSubBlock` to avoid redundant AstGen code for GenZir scopes. Allows adding/removing a field without possibility of accidentally introducing a bug of forgetting to set the new field. * Add to GenZir `nosuspend_node` and `suspend_node` in preparation for implementing `suspend` blocks and `nosuspend` blocks. * AstGen: fix assembly to support clobbers, multiple outputs, and outputs without `->` syntax. - `asm` and `asm_volatile` move to `Extended` enum with `small` being repurposed for a few things. This frees up 2 ZIR tags, 1 of which is used in this commit and 1 is leftover. * AstGen: fix `simple_types` incorrectly having multiple conflicting values for "undefined" and "null". - Also add "anyframe" to `simple_types`. * Add `anyframe_type` to type.zig, value.zig and `Zir.Inst.Ref`. - Also add i128 and u128 types to `Zir.Inst.Ref` and `simple_types`. * Sema/Zir: Fix incorrect math causing the function body to be messed up for Extended-encoded functions. * Zir: support `i32` fields for "extra" payloads.
1 parent 183ee09
src/AstGen.zig
@@ -723,8 +723,12 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn
         },
         .enum_literal => return simpleStrTok(gz, scope, rl, main_tokens[node], node, .enum_literal),
         .error_value => return simpleStrTok(gz, scope, rl, node_datas[node].rhs, node, .error_value),
-        .anyframe_literal => return astgen.failNode(node, "async and related features are not yet supported", .{}),
-        .anyframe_type => return astgen.failNode(node, "async and related features are not yet supported", .{}),
+        .anyframe_literal => return rvalue(gz, scope, rl, .anyframe_type, node),
+        .anyframe_type => {
+            const return_type = try typeExpr(gz, scope, node_datas[node].rhs);
+            const result = try gz.addUnNode(.anyframe_type, return_type, node);
+            return rvalue(gz, scope, rl, result, node);
+        },
         .@"catch" => {
             const catch_token = main_tokens[node];
             const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
@@ -1546,18 +1550,10 @@ fn labeledBlockExpr(
     const block_inst = try gz.addBlock(zir_tag, block_node);
     try gz.instructions.append(astgen.gpa, block_inst);
 
-    var block_scope: GenZir = .{
-        .parent = parent_scope,
-        .decl_node_index = gz.decl_node_index,
-        .astgen = gz.astgen,
-        .force_comptime = gz.force_comptime,
-        .ref_start_index = gz.ref_start_index,
-        .instructions = .{},
-        // TODO @as here is working around a stage1 miscompilation bug :(
-        .label = @as(?GenZir.Label, GenZir.Label{
-            .token = label_token,
-            .block_inst = block_inst,
-        }),
+    var block_scope = gz.makeSubBlock(parent_scope);
+    block_scope.label = GenZir.Label{
+        .token = label_token,
+        .block_inst = block_inst,
     };
     block_scope.setBreakResultLoc(rl);
     defer block_scope.instructions.deinit(astgen.gpa);
@@ -1695,10 +1691,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
             .array_type_sentinel,
             .elem_type,
             .indexable_ptr_len,
+            .anyframe_type,
             .as,
             .as_node,
-            .@"asm",
-            .asm_volatile,
             .bit_and,
             .bitcast,
             .bitcast_result_ptr,
@@ -2095,13 +2090,7 @@ fn varDecl(
 
             // Detect whether the initialization expression actually uses the
             // result location pointer.
-            var init_scope: GenZir = .{
-                .parent = scope,
-                .decl_node_index = gz.decl_node_index,
-                .force_comptime = gz.force_comptime,
-                .ref_start_index = gz.ref_start_index,
-                .astgen = astgen,
-            };
+            var init_scope = gz.makeSubBlock(scope);
             defer init_scope.instructions.deinit(gpa);
 
             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
@@ -3778,14 +3767,7 @@ fn tryExpr(
         return astgen.failNode(node, "invalid 'try' outside function scope", .{});
     };
 
-    var block_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var block_scope = parent_gz.makeSubBlock(scope);
     block_scope.setBreakResultLoc(rl);
     defer block_scope.instructions.deinit(astgen.gpa);
 
@@ -3811,28 +3793,14 @@ fn tryExpr(
     try parent_gz.instructions.append(astgen.gpa, block);
     try block_scope.setBlockBody(block);
 
-    var then_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = block_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var then_scope = parent_gz.makeSubBlock(scope);
     defer then_scope.instructions.deinit(astgen.gpa);
 
     const err_code = try then_scope.addUnNode(err_ops[1], operand, node);
     try genDefers(&then_scope, &fn_block.base, scope, err_code);
     const then_result = try then_scope.addUnNode(.ret_node, err_code, node);
 
-    var else_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = block_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var else_scope = parent_gz.makeSubBlock(scope);
     defer else_scope.instructions.deinit(astgen.gpa);
 
     block_scope.break_count += 1;
@@ -3878,14 +3846,7 @@ fn orelseCatchExpr(
     const astgen = parent_gz.astgen;
     const tree = &astgen.file.tree;
 
-    var block_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var block_scope = parent_gz.makeSubBlock(scope);
     block_scope.setBreakResultLoc(rl);
     defer block_scope.instructions.deinit(astgen.gpa);
 
@@ -3906,14 +3867,7 @@ fn orelseCatchExpr(
     try parent_gz.instructions.append(astgen.gpa, block);
     try block_scope.setBlockBody(block);
 
-    var then_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = block_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var then_scope = parent_gz.makeSubBlock(scope);
     defer then_scope.instructions.deinit(astgen.gpa);
 
     var err_val_scope: Scope.LocalVal = undefined;
@@ -3939,14 +3893,7 @@ fn orelseCatchExpr(
     // instructions into place until we know whether to keep store_to_block_ptr
     // instructions or not.
 
-    var else_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = block_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var else_scope = parent_gz.makeSubBlock(scope);
     defer else_scope.instructions.deinit(astgen.gpa);
 
     // This could be a pointer or value depending on `unwrap_op`.
@@ -4140,13 +4087,7 @@ fn boolBinOp(
     const lhs = try expr(gz, scope, bool_rl, node_datas[node].lhs);
     const bool_br = try gz.addBoolBr(zir_tag, lhs);
 
-    var rhs_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = gz.decl_node_index,
-        .astgen = gz.astgen,
-        .force_comptime = gz.force_comptime,
-        .ref_start_index = gz.ref_start_index,
-    };
+    var rhs_scope = gz.makeSubBlock(scope);
     defer rhs_scope.instructions.deinit(gz.astgen.gpa);
     const rhs = try expr(&rhs_scope, &rhs_scope.base, bool_rl, node_datas[node].rhs);
     _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
@@ -4167,14 +4108,7 @@ fn ifExpr(
     const tree = &astgen.file.tree;
     const token_tags = tree.tokens.items(.tag);
 
-    var block_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var block_scope = parent_gz.makeSubBlock(scope);
     block_scope.setBreakResultLoc(rl);
     defer block_scope.instructions.deinit(astgen.gpa);
 
@@ -4218,14 +4152,7 @@ fn ifExpr(
     try parent_gz.instructions.append(astgen.gpa, block);
     try block_scope.setBlockBody(block);
 
-    var then_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = block_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var then_scope = parent_gz.makeSubBlock(scope);
     defer then_scope.instructions.deinit(astgen.gpa);
 
     var payload_val_scope: Scope.LocalVal = undefined;
@@ -4273,14 +4200,7 @@ fn ifExpr(
     // instructions into place until we know whether to keep store_to_block_ptr
     // instructions or not.
 
-    var else_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = block_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var else_scope = parent_gz.makeSubBlock(scope);
     defer else_scope.instructions.deinit(astgen.gpa);
 
     const else_node = if_full.ast.else_expr;
@@ -4424,25 +4344,11 @@ fn whileExpr(
     const loop_block = try parent_gz.addBlock(loop_tag, node);
     try parent_gz.instructions.append(astgen.gpa, loop_block);
 
-    var loop_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var loop_scope = parent_gz.makeSubBlock(scope);
     loop_scope.setBreakResultLoc(rl);
     defer loop_scope.instructions.deinit(astgen.gpa);
 
-    var continue_scope: GenZir = .{
-        .parent = &loop_scope.base,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = loop_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var continue_scope = parent_gz.makeSubBlock(&loop_scope.base);
     defer continue_scope.instructions.deinit(astgen.gpa);
 
     const payload_is_ref = if (while_full.payload_token) |payload_token|
@@ -4505,14 +4411,7 @@ fn whileExpr(
         });
     }
 
-    var then_scope: GenZir = .{
-        .parent = &continue_scope.base,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = continue_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var then_scope = parent_gz.makeSubBlock(&continue_scope.base);
     defer then_scope.instructions.deinit(astgen.gpa);
 
     var payload_val_scope: Scope.LocalVal = undefined;
@@ -4557,14 +4456,7 @@ fn whileExpr(
     loop_scope.break_count += 1;
     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
 
-    var else_scope: GenZir = .{
-        .parent = &continue_scope.base,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = continue_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var else_scope = parent_gz.makeSubBlock(&continue_scope.base);
     defer else_scope.instructions.deinit(astgen.gpa);
 
     const else_node = while_full.ast.else_expr;
@@ -4659,25 +4551,11 @@ fn forExpr(
     const loop_block = try parent_gz.addBlock(loop_tag, node);
     try parent_gz.instructions.append(astgen.gpa, loop_block);
 
-    var loop_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var loop_scope = parent_gz.makeSubBlock(scope);
     loop_scope.setBreakResultLoc(rl);
     defer loop_scope.instructions.deinit(astgen.gpa);
 
-    var cond_scope: GenZir = .{
-        .parent = &loop_scope.base,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = loop_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
     defer cond_scope.instructions.deinit(astgen.gpa);
 
     // check condition i < array_expr.len
@@ -4714,14 +4592,7 @@ fn forExpr(
         });
     }
 
-    var then_scope: GenZir = .{
-        .parent = &cond_scope.base,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = cond_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
     defer then_scope.instructions.deinit(astgen.gpa);
 
     var payload_val_scope: Scope.LocalVal = undefined;
@@ -4773,14 +4644,7 @@ fn forExpr(
     loop_scope.break_count += 1;
     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
 
-    var else_scope: GenZir = .{
-        .parent = &cond_scope.base,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = cond_scope.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
     defer else_scope.instructions.deinit(astgen.gpa);
 
     const else_node = for_full.ast.else_expr;
@@ -5076,14 +4940,7 @@ fn switchExpr(
     var multi_cases_payload = ArrayListUnmanaged(u32){};
     defer multi_cases_payload.deinit(gpa);
 
-    var block_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var block_scope = parent_gz.makeSubBlock(scope);
     block_scope.setBreakResultLoc(rl);
     defer block_scope.instructions.deinit(gpa);
 
@@ -5091,14 +4948,7 @@ fn switchExpr(
     const switch_block = try parent_gz.addBlock(undefined, switch_node);
 
     // We re-use this same scope for all cases, including the special prong, if any.
-    var case_scope: GenZir = .{
-        .parent = &block_scope.base,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var case_scope = parent_gz.makeSubBlock(&block_scope.base);
     defer case_scope.instructions.deinit(gpa);
 
     // Do the else/`_` first because it goes first in the payload.
@@ -5846,57 +5696,109 @@ fn asmExpr(
     const tree = &astgen.file.tree;
     const main_tokens = tree.nodes.items(.main_token);
     const node_datas = tree.nodes.items(.data);
+    const token_tags = tree.tokens.items(.tag);
 
     const asm_source = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
 
     // See https://github.com/ziglang/zig/issues/215 and related issues discussing
-    // possible inline assembly improvements. Until this is settled, I am avoiding
-    // potentially wasting time implementing status quo assembly that is not used by
-    // any of the standard library.
-    if (full.outputs.len > 1) {
-        return astgen.failNode(node, "TODO more than 1 asm output", .{});
+    // possible inline assembly improvements. Until then here is status quo AstGen
+    // for assembly syntax. It's used by std lib crypto aesni.zig.
+
+    if (full.outputs.len > 32) {
+        return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
     }
-    const output: struct {
-        ty: Zir.Inst.Ref = .none,
-        constraint: u32 = 0,
-    } = if (full.outputs.len == 0) .{} else blk: {
-        const output_node = full.outputs[0];
-        const out_type_node = node_datas[output_node].lhs;
-        if (out_type_node == 0) {
-            return astgen.failNode(out_type_node, "TODO asm with non -> output", .{});
+    var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
+    const outputs = outputs_buffer[0..full.outputs.len];
+
+    var output_type_bits: u32 = 0;
+
+    for (full.outputs) |output_node, i| {
+        const symbolic_name = main_tokens[output_node];
+        const name = try gz.identAsString(symbolic_name);
+        const constraint_token = symbolic_name + 2;
+        const constraint = (try gz.strLitAsString(constraint_token)).index;
+        const has_arrow = token_tags[symbolic_name + 4] == .arrow;
+        if (has_arrow) {
+            output_type_bits |= @as(u32, 1) << @intCast(u5, i);
+            const out_type_node = node_datas[output_node].lhs;
+            const out_type_inst = try typeExpr(gz, scope, out_type_node);
+            outputs[i] = .{
+                .name = name,
+                .constraint = constraint,
+                .operand = out_type_inst,
+            };
+        } else {
+            const ident_token = symbolic_name + 4;
+            const str_index = try gz.identAsString(ident_token);
+            // TODO this needs extra code for local variables. Have a look at #215 and related
+            // issues and decide how to handle outputs. Do we want this to be identifiers?
+            // Or maybe we want to force this to be expressions with a pointer type.
+            // Until that is figured out this is only hooked up for referencing Decls.
+            const operand = try gz.addStrTok(.decl_ref, str_index, ident_token);
+            outputs[i] = .{
+                .name = name,
+                .constraint = constraint,
+                .operand = operand,
+            };
         }
-        const constraint_token = main_tokens[output_node] + 2;
-        break :blk .{
-            .ty = try typeExpr(gz, scope, out_type_node),
-            .constraint = (try gz.strLitAsString(constraint_token)).index,
-        };
-    };
+    }
 
-    const constraints = try arena.alloc(u32, full.inputs.len);
-    const args = try arena.alloc(Zir.Inst.Ref, full.inputs.len);
+    if (full.inputs.len > 32) {
+        return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
+    }
+    var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
+    const inputs = inputs_buffer[0..full.inputs.len];
+
+    for (full.inputs) |input_node, i| {
+        const symbolic_name = main_tokens[input_node];
+        const name = try gz.identAsString(symbolic_name);
+        const constraint_token = symbolic_name + 2;
+        const constraint = (try gz.strLitAsString(constraint_token)).index;
+        const has_arrow = token_tags[symbolic_name + 4] == .arrow;
+        const operand = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input_node].lhs);
+        inputs[i] = .{
+            .name = name,
+            .constraint = constraint,
+            .operand = operand,
+        };
+    }
 
-    for (full.inputs) |input, i| {
-        const constraint_token = main_tokens[input] + 2;
-        constraints[i] = (try gz.strLitAsString(constraint_token)).index;
-        args[i] = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[input].lhs);
+    var clobbers_buffer: [32]u32 = undefined;
+    var clobber_i: usize = 0;
+    if (full.first_clobber) |first_clobber| clobbers: {
+        // asm ("foo" ::: "a", "b")
+        // asm ("foo" ::: "a", "b",)
+        var tok_i = first_clobber;
+        while (true) : (tok_i += 1) {
+            if (clobber_i >= clobbers_buffer.len) {
+                return astgen.failTok(tok_i, "too many asm clobbers", .{});
+            }
+            clobbers_buffer[clobber_i] = (try gz.strLitAsString(tok_i)).index;
+            clobber_i += 1;
+            tok_i += 1;
+            switch (token_tags[tok_i]) {
+                .r_paren => break :clobbers,
+                .comma => {
+                    if (token_tags[tok_i + 1] == .r_paren) {
+                        break :clobbers;
+                    } else {
+                        continue;
+                    }
+                },
+                else => unreachable,
+            }
+        }
     }
 
-    const tag: Zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";
-    const result = try gz.addPlNode(tag, node, Zir.Inst.Asm{
+    const result = try gz.addAsm(.{
+        .node = node,
         .asm_source = asm_source,
-        .output_type = output.ty,
-        .args_len = @intCast(u32, full.inputs.len),
-        .clobbers_len = 0, // TODO implement asm clobbers
+        .is_volatile = full.volatile_token != null,
+        .output_type_bits = output_type_bits,
+        .outputs = outputs,
+        .inputs = inputs,
+        .clobbers = clobbers_buffer[0..clobber_i],
     });
-
-    try astgen.extra.ensureCapacity(astgen.gpa, astgen.extra.items.len +
-        args.len + constraints.len + @boolToInt(output.ty != .none));
-    if (output.ty != .none) {
-        astgen.extra.appendAssumeCapacity(output.constraint);
-    }
-    astgen.appendRefsAssumeCapacity(args);
-    astgen.extra.appendSliceAssumeCapacity(constraints);
-
     return rvalue(gz, scope, rl, result, node);
 }
 
@@ -5982,14 +5884,7 @@ fn asRlPtr(
     // as well as the store instruction, instead passing the result as an rvalue.
     const astgen = parent_gz.astgen;
 
-    var as_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = parent_gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = parent_gz.force_comptime,
-        .ref_start_index = parent_gz.ref_start_index,
-        .instructions = .{},
-    };
+    var as_scope = parent_gz.makeSubBlock(scope);
     defer as_scope.instructions.deinit(astgen.gpa);
 
     as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
@@ -6701,14 +6596,8 @@ fn cImport(
     const astgen = gz.astgen;
     const gpa = astgen.gpa;
 
-    var block_scope: GenZir = .{
-        .parent = scope,
-        .decl_node_index = gz.decl_node_index,
-        .astgen = astgen,
-        .force_comptime = true,
-        .ref_start_index = gz.ref_start_index,
-        .instructions = .{},
-    };
+    var block_scope = gz.makeSubBlock(scope);
+    block_scope.force_comptime = true;
     defer block_scope.instructions.deinit(gpa);
 
     const block_inst = try gz.addBlock(.c_import, node);
@@ -6802,43 +6691,44 @@ fn callExpr(
 }
 
 pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{
-    .{ "u8", .u8_type },
-    .{ "i8", .i8_type },
-    .{ "u16", .u16_type },
-    .{ "i16", .i16_type },
-    .{ "u32", .u32_type },
-    .{ "i32", .i32_type },
-    .{ "u64", .u64_type },
-    .{ "i64", .i64_type },
-    .{ "usize", .usize_type },
-    .{ "isize", .isize_type },
-    .{ "c_short", .c_short_type },
-    .{ "c_ushort", .c_ushort_type },
+    .{ "anyerror", .anyerror_type },
+    .{ "anyframe", .anyframe_type },
+    .{ "bool", .bool_type },
     .{ "c_int", .c_int_type },
-    .{ "c_uint", .c_uint_type },
     .{ "c_long", .c_long_type },
-    .{ "c_ulong", .c_ulong_type },
+    .{ "c_longdouble", .c_longdouble_type },
     .{ "c_longlong", .c_longlong_type },
+    .{ "c_short", .c_short_type },
+    .{ "c_uint", .c_uint_type },
+    .{ "c_ulong", .c_ulong_type },
     .{ "c_ulonglong", .c_ulonglong_type },
-    .{ "c_longdouble", .c_longdouble_type },
+    .{ "c_ushort", .c_ushort_type },
+    .{ "c_void", .c_void_type },
+    .{ "comptime_float", .comptime_float_type },
+    .{ "comptime_int", .comptime_int_type },
+    .{ "f128", .f128_type },
     .{ "f16", .f16_type },
     .{ "f32", .f32_type },
     .{ "f64", .f64_type },
-    .{ "f128", .f128_type },
-    .{ "c_void", .c_void_type },
-    .{ "bool", .bool_type },
-    .{ "void", .void_type },
-    .{ "type", .type_type },
-    .{ "anyerror", .anyerror_type },
-    .{ "comptime_int", .comptime_int_type },
-    .{ "comptime_float", .comptime_float_type },
+    .{ "false", .bool_false },
+    .{ "i16", .i16_type },
+    .{ "i32", .i32_type },
+    .{ "i64", .i64_type },
+    .{ "i128", .i128_type },
+    .{ "i8", .i8_type },
+    .{ "isize", .isize_type },
     .{ "noreturn", .noreturn_type },
-    .{ "null", .null_type },
-    .{ "undefined", .undefined_type },
-    .{ "undefined", .undef },
     .{ "null", .null_value },
     .{ "true", .bool_true },
-    .{ "false", .bool_false },
+    .{ "type", .type_type },
+    .{ "u16", .u16_type },
+    .{ "u32", .u32_type },
+    .{ "u64", .u64_type },
+    .{ "u128", .u128_type },
+    .{ "u8", .u8_type },
+    .{ "undefined", .undef },
+    .{ "usize", .usize_type },
+    .{ "void", .void_type },
 });
 
 fn nodeMayNeedMemoryLocation(tree: *const ast.Tree, start_node: ast.Node.Index) bool {
src/Module.zig
@@ -1085,6 +1085,21 @@ pub const Scope = struct {
         /// a result location pointer.
         labeled_store_to_block_ptr_list: ArrayListUnmanaged(Zir.Inst.Index) = .{},
 
+        suspend_node: ast.Node.Index = 0,
+        nosuspend_node: ast.Node.Index = 0,
+
+        pub fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
+            return .{
+                .force_comptime = gz.force_comptime,
+                .ref_start_index = gz.ref_start_index,
+                .decl_node_index = gz.decl_node_index,
+                .parent = scope,
+                .astgen = gz.astgen,
+                .suspend_node = gz.suspend_node,
+                .nosuspend_node = gz.nosuspend_node,
+            };
+        }
+
         pub const Label = struct {
             token: ast.TokenIndex,
             block_inst: Zir.Inst.Index,
@@ -1674,9 +1689,9 @@ pub const Scope = struct {
                     @as(usize, @boolToInt(args.type_inst != .none)) +
                     @as(usize, @boolToInt(args.align_inst != .none)),
             );
-            const payload_index = gz.astgen.addExtra(Zir.Inst.AllocExtended{
+            const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
                 .src_node = gz.nodeIndexToRelative(args.node),
-            }) catch unreachable; // ensureUnusedCapacity above
+            });
             if (args.type_inst != .none) {
                 astgen.extra.appendAssumeCapacity(@enumToInt(args.type_inst));
             }
@@ -1703,6 +1718,64 @@ pub const Scope = struct {
             return gz.indexToRef(new_index);
         }
 
+        pub fn addAsm(
+            gz: *GenZir,
+            args: struct {
+                /// Absolute node index. This function does the conversion to offset from Decl.
+                node: ast.Node.Index,
+                asm_source: Zir.Inst.Ref,
+                output_type_bits: u32,
+                is_volatile: bool,
+                outputs: []const Zir.Inst.Asm.Output,
+                inputs: []const Zir.Inst.Asm.Input,
+                clobbers: []const u32,
+            },
+        ) !Zir.Inst.Ref {
+            const astgen = gz.astgen;
+            const gpa = astgen.gpa;
+
+            try gz.instructions.ensureUnusedCapacity(gpa, 1);
+            try astgen.instructions.ensureUnusedCapacity(gpa, 1);
+            try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).Struct.fields.len +
+                args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).Struct.fields.len +
+                args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).Struct.fields.len +
+                args.clobbers.len);
+
+            const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
+                .src_node = gz.nodeIndexToRelative(args.node),
+                .asm_source = args.asm_source,
+                .output_type_bits = args.output_type_bits,
+            });
+            for (args.outputs) |output| {
+                _ = gz.astgen.addExtraAssumeCapacity(output);
+            }
+            for (args.inputs) |input| {
+                _ = gz.astgen.addExtraAssumeCapacity(input);
+            }
+            gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
+
+            //  * 0b00000000_000XXXXX - `outputs_len`.
+            //  * 0b000000XX_XXX00000 - `inputs_len`.
+            //  * 0b0XXXXX00_00000000 - `clobbers_len`.
+            //  * 0bX0000000_00000000 - is volatile
+            const small: u16 = @intCast(u16, args.outputs.len) |
+                @intCast(u16, args.inputs.len << 5) |
+                @intCast(u16, args.clobbers.len << 10) |
+                (@as(u16, @boolToInt(args.is_volatile)) << 15);
+
+            const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
+            astgen.instructions.appendAssumeCapacity(.{
+                .tag = .extended,
+                .data = .{ .extended = .{
+                    .opcode = .@"asm",
+                    .small = small,
+                    .operand = payload_index,
+                } },
+            });
+            gz.instructions.appendAssumeCapacity(new_index);
+            return gz.indexToRef(new_index);
+        }
+
         /// Note that this returns a `Zir.Inst.Index` not a ref.
         /// Does *not* append the block instruction to the scope.
         /// Leaves the `payload_index` field undefined.
@@ -2209,6 +2282,18 @@ pub const SrcLoc = struct {
                 const token_starts = tree.tokens.items(.start);
                 return token_starts[tok_index];
             },
+
+            .node_offset_anyframe_type => |node_off| {
+                const tree = src_loc.file_scope.tree;
+                const node_datas = tree.nodes.items(.data);
+                const node_tags = tree.nodes.items(.tag);
+                const parent_node = src_loc.declRelativeToNodeIndex(node_off);
+                const node = node_datas[parent_node].rhs;
+                const main_tokens = tree.nodes.items(.main_token);
+                const tok_index = main_tokens[node];
+                const token_starts = tree.tokens.items(.start);
+                return token_starts[tok_index];
+            },
         }
     }
 };
@@ -2368,6 +2453,12 @@ pub const LazySrcLoc = union(enum) {
     /// the return type node.
     /// The Decl is determined contextually.
     node_offset_fn_type_ret_ty: i32,
+    /// The source location points to the type expression of an `anyframe->T`
+    /// expression, found by taking this AST node index offset from the containing
+    /// Decl AST node, which points to a `anyframe->T` expression AST node. Next, navigate
+    /// to the type expression.
+    /// The Decl is determined contextually.
+    node_offset_anyframe_type: i32,
 
     /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
     pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
@@ -2407,6 +2498,7 @@ pub const LazySrcLoc = union(enum) {
             .node_offset_switch_range,
             .node_offset_fn_type_cc,
             .node_offset_fn_type_ret_ty,
+            .node_offset_anyframe_type,
             => .{
                 .file_scope = scope.getFileScope(),
                 .parent_decl_node = scope.srcDecl().?.src_node,
@@ -2453,6 +2545,7 @@ pub const LazySrcLoc = union(enum) {
             .node_offset_switch_range,
             .node_offset_fn_type_cc,
             .node_offset_fn_type_ret_ty,
+            .node_offset_anyframe_type,
             => .{
                 .file_scope = decl.getFileScope(),
                 .parent_decl_node = decl.src_node,
src/Sema.zig
@@ -138,14 +138,13 @@ pub fn analyzeBody(
             .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(block, inst),
             .alloc_mut                    => try sema.zirAllocMut(block, inst),
             .alloc_comptime               => try sema.zirAllocComptime(block, inst),
+            .anyframe_type                => try sema.zirAnyframeType(block, inst),
             .array_cat                    => try sema.zirArrayCat(block, inst),
             .array_mul                    => try sema.zirArrayMul(block, inst),
             .array_type                   => try sema.zirArrayType(block, inst),
             .array_type_sentinel          => try sema.zirArrayTypeSentinel(block, inst),
             .as                           => try sema.zirAs(block, inst),
             .as_node                      => try sema.zirAsNode(block, inst),
-            .@"asm"                       => try sema.zirAsm(block, inst, false),
-            .asm_volatile                 => try sema.zirAsm(block, inst, true),
             .bit_and                      => try sema.zirBitwise(block, inst, .bit_and),
             .bit_not                      => try sema.zirBitNot(block, inst),
             .bit_or                       => try sema.zirBitwise(block, inst, .bit_or),
@@ -518,6 +517,7 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
         .frame_address      => return sema.zirFrameAddress(    block, extended),
         .alloc              => return sema.zirAllocExtended(   block, extended),
         .builtin_extern     => return sema.zirBuiltinExtern(   block, extended),
+        .@"asm"             => return sema.zirAsm(             block, extended),
         .c_undef            => return sema.zirCUndef(          block, extended),
         .c_include          => return sema.zirCInclude(        block, extended),
         .c_define           => return sema.zirCDefine(         block, extended),
@@ -2173,6 +2173,19 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index)
     return sema.mod.constType(sema.arena, .unneeded, array_ty);
 }
 
+fn zirAnyframeType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const src = inst_data.src();
+    const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node };
+    const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
+    const anyframe_type = try Type.Tag.anyframe_T.create(sema.arena, return_type);
+
+    return sema.mod.constType(sema.arena, src, anyframe_type);
+}
+
 fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
@@ -4394,42 +4407,62 @@ fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*I
 fn zirAsm(
     sema: *Sema,
     block: *Scope.Block,
-    inst: Zir.Inst.Index,
-    is_volatile: bool,
+    extended: Zir.Inst.Extended.InstData,
 ) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
-    const src = inst_data.src();
-    const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node };
-    const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node };
-    const extra = sema.code.extraData(Zir.Inst.Asm, inst_data.payload_index);
+    const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
+    const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+    const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = extra.data.src_node };
+    const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = extra.data.src_node };
     const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source);
+    const outputs_len = @truncate(u5, extended.small);
+    const inputs_len = @truncate(u5, extended.small >> 5);
+    const clobbers_len = @truncate(u5, extended.small >> 10);
+    const is_volatile = @truncate(u1, extended.small >> 15) != 0;
+
+    if (outputs_len > 1) {
+        return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with more than 1 output", .{});
+    }
 
     var extra_i = extra.end;
+    var output_type_bits = extra.data.output_type_bits;
+
     const Output = struct { constraint: []const u8, ty: Type };
-    const output: ?Output = if (extra.data.output_type != .none) blk: {
-        const constraint = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
-        extra_i += 1;
+    const output: ?Output = if (outputs_len == 0) null else blk: {
+        const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
+        extra_i = output.end;
+
+        const is_type = @truncate(u1, output_type_bits) != 0;
+        output_type_bits >>= 1;
+
+        if (!is_type) {
+            return sema.mod.fail(&block.base, src, "TODO implement Sema for asm with non `->` output", .{});
+        }
+
+        const constraint = sema.code.nullTerminatedString(output.data.constraint);
         break :blk Output{
             .constraint = constraint,
-            .ty = try sema.resolveType(block, ret_ty_src, extra.data.output_type),
+            .ty = try sema.resolveType(block, ret_ty_src, output.data.operand),
         };
-    } else null;
+    };
 
-    const args = try sema.arena.alloc(*Inst, extra.data.args_len);
-    const inputs = try sema.arena.alloc([]const u8, extra.data.args_len);
-    const clobbers = try sema.arena.alloc([]const u8, extra.data.clobbers_len);
+    const args = try sema.arena.alloc(*Inst, inputs_len);
+    const inputs = try sema.arena.alloc([]const u8, inputs_len);
 
-    for (args) |*arg| {
-        arg.* = try sema.resolveInst(@intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]));
-        extra_i += 1;
-    }
-    for (inputs) |*name| {
-        name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
-        extra_i += 1;
+    for (args) |*arg, arg_i| {
+        const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
+        extra_i = input.end;
+
+        const name = sema.code.nullTerminatedString(input.data.name);
+        _ = name; // TODO: use the name
+
+        arg.* = try sema.resolveInst(input.data.operand);
+        inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint);
     }
+
+    const clobbers = try sema.arena.alloc([]const u8, clobbers_len);
     for (clobbers) |*name| {
         name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]);
         extra_i += 1;
@@ -5408,7 +5441,7 @@ fn zirFuncExtended(
 
     var extra_index: usize = extra.end;
     if (small.has_lib_name) {
-        const lib_name = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+        const lib_name = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
         extra_index += 1;
         return sema.mod.fail(&block.base, src, "TODO: implement Sema func lib name", .{});
     }
@@ -5428,7 +5461,7 @@ fn zirFuncExtended(
     } else .Unspecified;
 
     const param_types = sema.code.refSlice(extra_index, extra.data.param_types_len);
-    extra_index += 1;
+    extra_index += param_types.len;
 
     const body = sema.code.extra[extra_index..][0..extra.data.body_len];
 
src/type.zig
@@ -95,6 +95,8 @@ pub const Type = extern union {
 
             .anyerror_void_error_union, .error_union => return .ErrorUnion,
 
+            .anyframe_T, .@"anyframe" => return .AnyFrame,
+
             .empty_struct,
             .empty_struct_literal,
             .@"struct",
@@ -620,6 +622,7 @@ pub const Type = extern union {
             .call_options,
             .export_options,
             .extern_options,
+            .@"anyframe",
             => unreachable,
 
             .array_u8,
@@ -637,6 +640,7 @@ pub const Type = extern union {
             .optional,
             .optional_single_mut_pointer,
             .optional_single_const_pointer,
+            .anyframe_T,
             => return self.copyPayloadShallow(allocator, Payload.ElemType),
 
             .int_signed,
@@ -754,6 +758,7 @@ pub const Type = extern union {
                 .void,
                 .type,
                 .anyerror,
+                .@"anyframe",
                 .comptime_int,
                 .comptime_float,
                 .noreturn,
@@ -820,6 +825,12 @@ pub const Type = extern union {
                     continue;
                 },
 
+                .anyframe_T => {
+                    const return_type = ty.castTag(.anyframe_T).?.data;
+                    try writer.print("anyframe->", .{});
+                    ty = return_type;
+                    continue;
+                },
                 .array_u8 => {
                     const len = ty.castTag(.array_u8).?.data;
                     return writer.print("[{d}]u8", .{len});
@@ -994,6 +1005,7 @@ pub const Type = extern union {
             .void => return Value.initTag(.void_type),
             .type => return Value.initTag(.type_type),
             .anyerror => return Value.initTag(.anyerror_type),
+            .@"anyframe" => return Value.initTag(.anyframe_type),
             .comptime_int => return Value.initTag(.comptime_int_type),
             .comptime_float => return Value.initTag(.comptime_float_type),
             .noreturn => return Value.initTag(.noreturn_type),
@@ -1075,6 +1087,8 @@ pub const Type = extern union {
             .call_options,
             .export_options,
             .extern_options,
+            .@"anyframe",
+            .anyframe_T,
             => true,
 
             .@"struct" => {
@@ -1223,6 +1237,8 @@ pub const Type = extern union {
             .pointer,
             .manyptr_u8,
             .manyptr_const_u8,
+            .@"anyframe",
+            .anyframe_T,
             => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
 
             .c_short => return @divExact(CType.short.sizeInBits(target), 8),
@@ -1388,7 +1404,11 @@ pub const Type = extern union {
             .i64, .u64 => return 8,
             .u128, .i128 => return 16,
 
-            .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
+            .isize,
+            .usize,
+            .@"anyframe",
+            .anyframe_T,
+            => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
 
             .const_slice,
             .mut_slice,
@@ -1536,7 +1556,11 @@ pub const Type = extern union {
             .i64, .u64, .f64 => 64,
             .u128, .i128, .f128 => 128,
 
-            .isize, .usize => target.cpu.arch.ptrBitWidth(),
+            .isize,
+            .usize,
+            .@"anyframe",
+            .anyframe_T,
+            => target.cpu.arch.ptrBitWidth(),
 
             .const_slice,
             .mut_slice,
@@ -2256,6 +2280,8 @@ pub const Type = extern union {
             .call_options,
             .export_options,
             .extern_options,
+            .@"anyframe",
+            .anyframe_T,
             => return null,
 
             .@"struct" => {
@@ -2666,9 +2692,10 @@ pub const Type = extern union {
         comptime_int,
         comptime_float,
         noreturn,
+        @"anyframe",
+        @"null",
+        @"undefined",
         enum_literal,
-        manyptr_u8,
-        manyptr_const_u8,
         atomic_ordering,
         atomic_rmw_op,
         calling_convention,
@@ -2677,15 +2704,15 @@ pub const Type = extern union {
         call_options,
         export_options,
         extern_options,
-        @"null",
-        @"undefined",
+        manyptr_u8,
+        manyptr_const_u8,
         fn_noreturn_no_args,
         fn_void_no_args,
         fn_naked_noreturn_no_args,
         fn_ccc_void_no_args,
         single_const_pointer_to_comptime_int,
-        anyerror_void_error_union,
         const_slice_u8,
+        anyerror_void_error_union,
         /// This is a special type for variadic parameters of a function call.
         /// Casts to it will validate that the type can be passed to a c calling convetion function.
         var_args_param,
@@ -2719,6 +2746,7 @@ pub const Type = extern union {
         optional_single_mut_pointer,
         optional_single_const_pointer,
         error_union,
+        anyframe_T,
         error_set,
         error_set_single,
         empty_struct,
@@ -2790,6 +2818,7 @@ pub const Type = extern union {
                 .call_options,
                 .export_options,
                 .extern_options,
+                .@"anyframe",
                 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
 
                 .array_u8,
@@ -2807,6 +2836,7 @@ pub const Type = extern union {
                 .optional,
                 .optional_single_mut_pointer,
                 .optional_single_const_pointer,
+                .anyframe_T,
                 => Payload.ElemType,
 
                 .int_signed,
src/value.zig
@@ -55,17 +55,10 @@ pub const Value = extern union {
         comptime_int_type,
         comptime_float_type,
         noreturn_type,
+        anyframe_type,
         null_type,
         undefined_type,
-        fn_noreturn_no_args_type,
-        fn_void_no_args_type,
-        fn_naked_noreturn_no_args_type,
-        fn_ccc_void_no_args_type,
-        single_const_pointer_to_comptime_int_type,
-        const_slice_u8_type,
         enum_literal_type,
-        manyptr_u8_type,
-        manyptr_const_u8_type,
         atomic_ordering_type,
         atomic_rmw_op_type,
         calling_convention_type,
@@ -74,6 +67,14 @@ pub const Value = extern union {
         call_options_type,
         export_options_type,
         extern_options_type,
+        manyptr_u8_type,
+        manyptr_const_u8_type,
+        fn_noreturn_no_args_type,
+        fn_void_no_args_type,
+        fn_naked_noreturn_no_args_type,
+        fn_ccc_void_no_args_type,
+        single_const_pointer_to_comptime_int_type,
+        const_slice_u8_type,
 
         undef,
         zero,
@@ -166,6 +167,7 @@ pub const Value = extern union {
                 .fn_naked_noreturn_no_args_type,
                 .fn_ccc_void_no_args_type,
                 .single_const_pointer_to_comptime_int_type,
+                .anyframe_type,
                 .const_slice_u8_type,
                 .enum_literal_type,
                 .undef,
@@ -334,6 +336,7 @@ pub const Value = extern union {
             .fn_naked_noreturn_no_args_type,
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
+            .anyframe_type,
             .const_slice_u8_type,
             .enum_literal_type,
             .undef,
@@ -502,6 +505,7 @@ pub const Value = extern union {
             .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
             .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
             .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
+            .anyframe_type => return out_stream.writeAll("anyframe"),
             .const_slice_u8_type => return out_stream.writeAll("[]const u8"),
             .enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
             .manyptr_u8_type => return out_stream.writeAll("[*]u8"),
@@ -633,6 +637,7 @@ pub const Value = extern union {
             .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
             .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
             .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
+            .anyframe_type => Type.initTag(.@"anyframe"),
             .const_slice_u8_type => Type.initTag(.const_slice_u8),
             .enum_literal_type => Type.initTag(.enum_literal),
             .manyptr_u8_type => Type.initTag(.manyptr_u8),
@@ -1070,6 +1075,7 @@ pub const Value = extern union {
             .fn_naked_noreturn_no_args_type,
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
+            .anyframe_type,
             .const_slice_u8_type,
             .enum_literal_type,
             .ty,
@@ -1338,6 +1344,7 @@ pub const Value = extern union {
             .fn_naked_noreturn_no_args_type,
             .fn_ccc_void_no_args_type,
             .single_const_pointer_to_comptime_int_type,
+            .anyframe_type,
             .const_slice_u8_type,
             .enum_literal_type,
             .manyptr_u8_type,
src/Zir.zig
@@ -60,7 +60,8 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
         @field(result, field.name) = switch (field.field_type) {
             u32 => code.extra[i],
             Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
-            else => unreachable,
+            i32 => @bitCast(i32, code.extra[i]),
+            else => @compileError("bad field type"),
         };
         i += 1;
     }
@@ -165,18 +166,15 @@ pub const Inst = struct {
         /// error if the indexable object is not indexable.
         /// Uses the `un_node` field. The AST node is the for loop node.
         indexable_ptr_len,
+        /// Create a `anyframe->T` type.
+        /// Uses the `un_node` field.
+        anyframe_type,
         /// Type coercion. No source location attached.
         /// Uses the `bin` field.
         as,
         /// Type coercion to the function's return type.
         /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
         as_node,
-        /// Inline assembly. Non-volatile.
-        /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node.
-        @"asm",
-        /// Inline assembly with the volatile attribute.
-        /// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node.
-        asm_volatile,
         /// Bitwise AND. `&`
         bit_and,
         /// Bitcast a value to a different type.
@@ -967,10 +965,9 @@ pub const Inst = struct {
                 .array_type_sentinel,
                 .elem_type,
                 .indexable_ptr_len,
+                .anyframe_type,
                 .as,
                 .as_node,
-                .@"asm",
-                .asm_volatile,
                 .bit_and,
                 .bitcast,
                 .bitcast_result_ptr,
@@ -1259,6 +1256,14 @@ pub const Inst = struct {
         /// The `@extern` builtin.
         /// `operand` is payload index to `BinNode`.
         builtin_extern,
+        /// Inline assembly.
+        /// `small`:
+        ///  * 0b00000000_000XXXXX - `outputs_len`.
+        ///  * 0b000000XX_XXX00000 - `inputs_len`.
+        ///  * 0b0XXXXX00_00000000 - `clobbers_len`.
+        ///  * 0bX0000000_00000000 - is volatile
+        /// `operand` is payload index to `Asm`.
+        @"asm",
         /// `operand` is payload index to `UnNode`.
         c_undef,
         /// `operand` is payload index to `UnNode`.
@@ -1313,6 +1318,8 @@ pub const Inst = struct {
         i32_type,
         u64_type,
         i64_type,
+        u128_type,
+        i128_type,
         usize_type,
         isize_type,
         c_short_type,
@@ -1336,17 +1343,10 @@ pub const Inst = struct {
         comptime_int_type,
         comptime_float_type,
         noreturn_type,
+        anyframe_type,
         null_type,
         undefined_type,
-        fn_noreturn_no_args_type,
-        fn_void_no_args_type,
-        fn_naked_noreturn_no_args_type,
-        fn_ccc_void_no_args_type,
-        single_const_pointer_to_comptime_int_type,
-        const_slice_u8_type,
         enum_literal_type,
-        manyptr_u8_type,
-        manyptr_const_u8_type,
         atomic_ordering_type,
         atomic_rmw_op_type,
         calling_convention_type,
@@ -1355,6 +1355,14 @@ pub const Inst = struct {
         call_options_type,
         export_options_type,
         extern_options_type,
+        manyptr_u8_type,
+        manyptr_const_u8_type,
+        fn_noreturn_no_args_type,
+        fn_void_no_args_type,
+        fn_naked_noreturn_no_args_type,
+        fn_ccc_void_no_args_type,
+        single_const_pointer_to_comptime_int_type,
+        const_slice_u8_type,
 
         /// `undefined` (untyped)
         undef,
@@ -1418,6 +1426,14 @@ pub const Inst = struct {
                 .ty = Type.initTag(.type),
                 .val = Value.initTag(.i64_type),
             },
+            .u128_type = .{
+                .ty = Type.initTag(.type),
+                .val = Value.initTag(.u128_type),
+            },
+            .i128_type = .{
+                .ty = Type.initTag(.type),
+                .val = Value.initTag(.i128_type),
+            },
             .usize_type = .{
                 .ty = Type.initTag(.type),
                 .val = Value.initTag(.usize_type),
@@ -1510,6 +1526,10 @@ pub const Inst = struct {
                 .ty = Type.initTag(.type),
                 .val = Value.initTag(.noreturn_type),
             },
+            .anyframe_type = .{
+                .ty = Type.initTag(.type),
+                .val = Value.initTag(.anyframe_type),
+            },
             .null_type = .{
                 .ty = Type.initTag(.type),
                 .val = Value.initTag(.null_type),
@@ -1806,17 +1826,35 @@ pub const Inst = struct {
         }
     };
 
-    /// Stored in extra. Trailing is:
-    /// * output_constraint: u32 // index into string_bytes (null terminated) if output is present
-    /// * arg: Ref // for every args_len.
-    /// * constraint: u32 // index into string_bytes (null terminated) for every args_len.
-    /// * clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
+    /// Trailing:
+    /// 0. Output for every outputs_len
+    /// 1. Input for every inputs_len
+    /// 2. clobber: u32 // index into string_bytes (null terminated) for every clobbers_len.
     pub const Asm = struct {
+        src_node: i32,
         asm_source: Ref,
-        /// May be omitted.
-        output_type: Ref,
-        args_len: u32,
-        clobbers_len: u32,
+        /// 1 bit for each outputs_len: whether it uses `-> T` or not.
+        ///   0b0 - operand is a pointer to where to store the output.
+        ///   0b1 - operand is a type; asm expression has the output as the result.
+        /// 0b0X is the first output, 0bX0 is the second, etc.
+        output_type_bits: u32,
+
+        pub const Output = struct {
+            /// index into string_bytes (null terminated)
+            name: u32,
+            /// index into string_bytes (null terminated)
+            constraint: u32,
+            /// How to interpret this is determined by `output_type_bits`.
+            operand: Ref,
+        };
+
+        pub const Input = struct {
+            /// index into string_bytes (null terminated)
+            name: u32,
+            /// index into string_bytes (null terminated)
+            constraint: u32,
+            operand: Ref,
+        };
     };
 
     /// Trailing:
@@ -2298,6 +2336,7 @@ const Writer = struct {
             .alloc_mut,
             .alloc_comptime,
             .indexable_ptr_len,
+            .anyframe_type,
             .bit_not,
             .bool_not,
             .negate,
@@ -2430,10 +2469,6 @@ const Writer = struct {
             .builtin_async_call,
             => try self.writePlNode(stream, inst),
 
-            .@"asm",
-            .asm_volatile,
-            => try self.writePlNodeAsm(stream, inst),
-
             .error_set_decl => try self.writePlNodeErrorSetDecl(stream, inst),
 
             .add_with_overflow,
@@ -2603,7 +2638,9 @@ const Writer = struct {
             .builtin_src,
             => try self.writeExtNode(stream, extended),
 
-            .func,
+            .@"asm" => try self.writeAsm(stream, extended),
+            .func => try self.writeFuncExtended(stream, extended),
+
             .alloc,
             .builtin_extern,
             .c_undef,
@@ -2794,49 +2831,74 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
-    fn writePlNodeAsm(self: *Writer, stream: anytype, inst: Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
-        const extra = self.code.extraData(Inst.Asm, inst_data.payload_index);
-        var extra_i: usize = extra.end;
+    fn writeAsm(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void {
+        const extra = self.code.extraData(Inst.Asm, extended.operand);
+        const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+        const outputs_len = @truncate(u5, extended.small);
+        const inputs_len = @truncate(u5, extended.small >> 5);
+        const clobbers_len = @truncate(u5, extended.small >> 10);
+        const is_volatile = @truncate(u1, extended.small >> 15) != 0;
 
-        if (extra.data.output_type != .none) {
-            const constraint_str_index = self.code.extra[extra_i];
-            extra_i += 1;
-            const constraint = self.code.nullTerminatedString(constraint_str_index);
-            try stream.print("\"{}\"->", .{std.zig.fmtEscapes(constraint)});
-            try self.writeInstRef(stream, extra.data.output_type);
-            try stream.writeAll(", ");
-        }
+        try self.writeFlag(stream, "volatile, ", is_volatile);
+        try self.writeInstRef(stream, extra.data.asm_source);
+        try stream.writeAll(", ");
+
+        var extra_i: usize = extra.end;
+        var output_type_bits = extra.data.output_type_bits;
         {
             var i: usize = 0;
-            while (i < extra.data.args_len) : (i += 1) {
-                const arg = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_i]);
-                extra_i += 1;
-                try self.writeInstRef(stream, arg);
-                try stream.writeAll(", ");
+            while (i < outputs_len) : (i += 1) {
+                const output = self.code.extraData(Inst.Asm.Output, extra_i);
+                extra_i = output.end;
+
+                const is_type = @truncate(u1, output_type_bits) != 0;
+                output_type_bits >>= 1;
+
+                const name = self.code.nullTerminatedString(output.data.name);
+                const constraint = self.code.nullTerminatedString(output.data.constraint);
+                try stream.print("output({}, \"{}\", ", .{
+                    std.zig.fmtId(name), std.zig.fmtEscapes(constraint),
+                });
+                try self.writeFlag(stream, "->", is_type);
+                try self.writeInstRef(stream, output.data.operand);
+                try stream.writeAll(")");
+                if (i + 1 < outputs_len) {
+                    try stream.writeAll("), ");
+                }
             }
         }
         {
             var i: usize = 0;
-            while (i < extra.data.args_len) : (i += 1) {
-                const str_index = self.code.extra[extra_i];
-                extra_i += 1;
-                const constraint = self.code.nullTerminatedString(str_index);
-                try stream.print("\"{}\", ", .{std.zig.fmtEscapes(constraint)});
+            while (i < inputs_len) : (i += 1) {
+                const input = self.code.extraData(Inst.Asm.Input, extra_i);
+                extra_i = input.end;
+
+                const name = self.code.nullTerminatedString(input.data.name);
+                const constraint = self.code.nullTerminatedString(input.data.constraint);
+                try stream.print("input({}, \"{}\", ", .{
+                    std.zig.fmtId(name), std.zig.fmtEscapes(constraint),
+                });
+                try self.writeInstRef(stream, input.data.operand);
+                try stream.writeAll(")");
+                if (i + 1 < inputs_len) {
+                    try stream.writeAll(", ");
+                }
             }
         }
         {
             var i: usize = 0;
-            while (i < extra.data.clobbers_len) : (i += 1) {
+            while (i < clobbers_len) : (i += 1) {
                 const str_index = self.code.extra[extra_i];
                 extra_i += 1;
                 const clobber = self.code.nullTerminatedString(str_index);
-                try stream.print("{}, ", .{std.zig.fmtId(clobber)});
+                try stream.print("{}", .{std.zig.fmtId(clobber)});
+                if (i + 1 < clobbers_len) {
+                    try stream.writeAll(", ");
+                }
             }
         }
-        try self.writeInstRef(stream, extra.data.asm_source);
         try stream.writeAll(") ");
-        try self.writeSrc(stream, inst_data.src());
+        try self.writeSrc(stream, src);
     }
 
     fn writePlNodeOverflowArithmetic(self: *Writer, stream: anytype, inst: Inst.Index) !void {
@@ -3467,6 +3529,40 @@ const Writer = struct {
         );
     }
 
+    fn writeFuncExtended(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void {
+        const extra = self.code.extraData(Inst.ExtendedFunc, extended.operand);
+        const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+        const small = @bitCast(Inst.ExtendedFunc.Small, extended.small);
+
+        var extra_index: usize = extra.end;
+        if (small.has_lib_name) {
+            const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
+            extra_index += 1;
+            try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)});
+        }
+        const cc: Inst.Ref = if (!small.has_cc) .none else blk: {
+            const cc = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
+            extra_index += 1;
+            break :blk cc;
+        };
+
+        const param_types = self.code.refSlice(extra_index, extra.data.param_types_len);
+        extra_index += param_types.len;
+
+        const body = self.code.extra[extra_index..][0..extra.data.body_len];
+
+        return self.writeFuncCommon(
+            stream,
+            param_types,
+            extra.data.return_type,
+            small.is_inferred_error,
+            small.is_var_args,
+            cc,
+            body,
+            src,
+        );
+    }
+
     fn writeBoolBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[inst].bool_br;
         const extra = self.code.extraData(Inst.Block, inst_data.payload_index);