| author | |
| committer | |
| log | 2e1c16d64979c15604b90128fbd63d26ab4b796d |
| tree | a6cea3b541c8902a8a6b63a17e2b4a2b076a1b08 |
| parent | 09d93ec845f2f1adaefc512fccaeaa0ea8beed61 |
| parent | 4e1e5ab6221b72ef2be9f1fb40c2e6d1235718fe |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
AIR independence day15 files changed, 562 insertions(+), 403 deletions(-)
lib/std/array_list.zig+8| ... | ... | @@ -780,6 +780,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 780 | 780 | pub fn allocatedSlice(self: Self) Slice { |
| 781 | 781 | return self.items.ptr[0..self.capacity]; |
| 782 | 782 | } |
| 783 | ||
| 784 | /// Returns a slice of only the extra capacity after items. | |
| 785 | /// This can be useful for writing directly into an ArrayList. | |
| 786 | /// Note that such an operation must be followed up with a direct | |
| 787 | /// modification of `self.items.len`. | |
| 788 | pub fn unusedCapacitySlice(self: Self) Slice { | |
| 789 | return self.allocatedSlice()[self.items.len..]; | |
| 790 | } | |
| 783 | 791 | }; |
| 784 | 792 | } |
| 785 | 793 |
src/Air.zig+20-13| ... | ... | @@ -32,8 +32,7 @@ pub const Inst = struct { |
| 32 | 32 | /// The first N instructions in the main block must be one arg instruction per |
| 33 | 33 | /// function parameter. This makes function parameters participate in |
| 34 | 34 | /// liveness analysis without any special handling. |
| 35 | /// Uses the `ty_str` field. | |
| 36 | /// The string is the parameter name. | |
| 35 | /// Uses the `ty` field. | |
| 37 | 36 | arg, |
| 38 | 37 | /// Float or integer addition. For integers, wrapping is undefined behavior. |
| 39 | 38 | /// Both operands are guaranteed to be the same type, and the result type |
| ... | ... | @@ -621,11 +620,6 @@ pub const Inst = struct { |
| 621 | 620 | // Index into a different array. |
| 622 | 621 | payload: u32, |
| 623 | 622 | }, |
| 624 | ty_str: struct { | |
| 625 | ty: Ref, | |
| 626 | // ZIR string table index. | |
| 627 | str: u32, | |
| 628 | }, | |
| 629 | 623 | br: struct { |
| 630 | 624 | block_inst: Index, |
| 631 | 625 | operand: Ref, |
| ... | ... | @@ -709,11 +703,25 @@ pub const Bin = struct { |
| 709 | 703 | /// Trailing: |
| 710 | 704 | /// 0. `Inst.Ref` for every outputs_len |
| 711 | 705 | /// 1. `Inst.Ref` for every inputs_len |
| 706 | /// 2. for every outputs_len | |
| 707 | /// - constraint: memory at this position is reinterpreted as a null | |
| 708 | /// terminated string. pad to the next u32 after the null byte. | |
| 709 | /// 3. for every inputs_len | |
| 710 | /// - constraint: memory at this position is reinterpreted as a null | |
| 711 | /// terminated string. pad to the next u32 after the null byte. | |
| 712 | /// 4. for every clobbers_len | |
| 713 | /// - clobber_name: memory at this position is reinterpreted as a null | |
| 714 | /// terminated string. pad to the next u32 after the null byte. | |
| 715 | /// 5. A number of u32 elements follow according to the equation `(source_len + 3) / 4`. | |
| 716 | /// Memory starting at this position is reinterpreted as the source bytes. | |
| 712 | 717 | pub const Asm = struct { |
| 713 | /// Index to the corresponding ZIR instruction. | |
| 714 | /// `asm_source`, `outputs_len`, `inputs_len`, `clobbers_len`, `is_volatile`, and | |
| 715 | /// clobbers are found via here. | |
| 716 | zir_index: u32, | |
| 718 | /// Length of the assembly source in bytes. | |
| 719 | source_len: u32, | |
| 720 | outputs_len: u32, | |
| 721 | inputs_len: u32, | |
| 722 | /// The MSB is `is_volatile`. | |
| 723 | /// The rest of the bits are `clobbers_len`. | |
| 724 | flags: u32, | |
| 717 | 725 | }; |
| 718 | 726 | |
| 719 | 727 | pub const Cmpxchg = struct { |
| ... | ... | @@ -765,8 +773,6 @@ pub fn typeOf(air: Air, inst: Air.Inst.Ref) Type { |
| 765 | 773 | pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 766 | 774 | const datas = air.instructions.items(.data); |
| 767 | 775 | switch (air.instructions.items(.tag)[inst]) { |
| 768 | .arg => return air.getRefType(datas[inst].ty_str.ty), | |
| 769 | ||
| 770 | 776 | .add, |
| 771 | 777 | .addwrap, |
| 772 | 778 | .add_sat, |
| ... | ... | @@ -833,6 +839,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 833 | 839 | |
| 834 | 840 | .alloc, |
| 835 | 841 | .ret_ptr, |
| 842 | .arg, | |
| 836 | 843 | => return datas[inst].ty, |
| 837 | 844 | |
| 838 | 845 | .assembly, |
src/Compilation.zig+2-2| ... | ... | @@ -2778,7 +2778,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2778 | 2778 | errdefer if (!liveness_frame_ended) liveness_frame.end(); |
| 2779 | 2779 | |
| 2780 | 2780 | log.debug("analyze liveness of {s}", .{decl.name}); |
| 2781 | var liveness = try Liveness.analyze(gpa, air, decl.getFileScope().zir); | |
| 2781 | var liveness = try Liveness.analyze(gpa, air); | |
| 2782 | 2782 | defer liveness.deinit(gpa); |
| 2783 | 2783 | |
| 2784 | 2784 | liveness_frame.end(); |
| ... | ... | @@ -2786,7 +2786,7 @@ fn processOneJob(comp: *Compilation, job: Job, main_progress_node: *std.Progress |
| 2786 | 2786 | |
| 2787 | 2787 | if (builtin.mode == .Debug and comp.verbose_air) { |
| 2788 | 2788 | std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name}); |
| 2789 | @import("print_air.zig").dump(gpa, air, decl.getFileScope().zir, liveness); | |
| 2789 | @import("print_air.zig").dump(gpa, air, liveness); | |
| 2790 | 2790 | std.debug.print("# End Function AIR: {s}\n\n", .{decl.name}); |
| 2791 | 2791 | } |
| 2792 | 2792 |
src/Liveness.zig+23-15| ... | ... | @@ -12,7 +12,6 @@ const log = std.log.scoped(.liveness); |
| 12 | 12 | const assert = std.debug.assert; |
| 13 | 13 | const Allocator = std.mem.Allocator; |
| 14 | 14 | const Air = @import("Air.zig"); |
| 15 | const Zir = @import("Zir.zig"); | |
| 16 | 15 | const Log2Int = std.math.Log2Int; |
| 17 | 16 | |
| 18 | 17 | /// This array is split into sets of 4 bits per AIR instruction. |
| ... | ... | @@ -52,7 +51,7 @@ pub const SwitchBr = struct { |
| 52 | 51 | else_death_count: u32, |
| 53 | 52 | }; |
| 54 | 53 | |
| 55 | pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { | |
| 54 | pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness { | |
| 56 | 55 | const tracy = trace(@src()); |
| 57 | 56 | defer tracy.end(); |
| 58 | 57 | |
| ... | ... | @@ -66,7 +65,6 @@ pub fn analyze(gpa: Allocator, air: Air, zir: Zir) Allocator.Error!Liveness { |
| 66 | 65 | ), |
| 67 | 66 | .extra = .{}, |
| 68 | 67 | .special = .{}, |
| 69 | .zir = &zir, | |
| 70 | 68 | }; |
| 71 | 69 | errdefer gpa.free(a.tomb_bits); |
| 72 | 70 | errdefer a.special.deinit(gpa); |
| ... | ... | @@ -157,7 +155,6 @@ const Analysis = struct { |
| 157 | 155 | tomb_bits: []usize, |
| 158 | 156 | special: std.AutoHashMapUnmanaged(Air.Inst.Index, u32), |
| 159 | 157 | extra: std.ArrayListUnmanaged(u32), |
| 160 | zir: *const Zir, | |
| 161 | 158 | |
| 162 | 159 | fn storeTombBits(a: *Analysis, inst: Air.Inst.Index, tomb_bits: Bpi) void { |
| 163 | 160 | const usize_index = (inst * bpi) / @bitSizeOf(usize); |
| ... | ... | @@ -446,15 +443,24 @@ fn analyzeInst( |
| 446 | 443 | }, |
| 447 | 444 | .assembly => { |
| 448 | 445 | const extra = a.air.extraData(Air.Asm, inst_datas[inst].ty_pl.payload); |
| 449 | const extended = a.zir.instructions.items(.data)[extra.data.zir_index].extended; | |
| 450 | const outputs_len = @truncate(u5, extended.small); | |
| 451 | const inputs_len = @truncate(u5, extended.small >> 5); | |
| 452 | const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end..][0..outputs_len]); | |
| 453 | const args = @bitCast([]const Air.Inst.Ref, a.air.extra[extra.end + outputs.len ..][0..inputs_len]); | |
| 454 | if (outputs.len + args.len <= bpi - 1) { | |
| 446 | var extra_i: usize = extra.end; | |
| 447 | const outputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 448 | extra_i += outputs.len; | |
| 449 | const inputs = @bitCast([]const Air.Inst.Ref, a.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 450 | extra_i += inputs.len; | |
| 451 | ||
| 452 | simple: { | |
| 455 | 453 | var buf = [1]Air.Inst.Ref{.none} ** (bpi - 1); |
| 456 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | |
| 457 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | |
| 454 | var buf_index: usize = 0; | |
| 455 | for (outputs) |output| { | |
| 456 | if (output != .none) { | |
| 457 | if (buf_index >= buf.len) break :simple; | |
| 458 | buf[buf_index] = output; | |
| 459 | buf_index += 1; | |
| 460 | } | |
| 461 | } | |
| 462 | if (buf_index + inputs.len > buf.len) break :simple; | |
| 463 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | |
| 458 | 464 | return trackOperands(a, new_set, inst, main_tomb, buf); |
| 459 | 465 | } |
| 460 | 466 | var extra_tombs: ExtraTombs = .{ |
| ... | ... | @@ -464,10 +470,12 @@ fn analyzeInst( |
| 464 | 470 | .main_tomb = main_tomb, |
| 465 | 471 | }; |
| 466 | 472 | for (outputs) |output| { |
| 467 | try extra_tombs.feed(output); | |
| 473 | if (output != .none) { | |
| 474 | try extra_tombs.feed(output); | |
| 475 | } | |
| 468 | 476 | } |
| 469 | for (args) |arg| { | |
| 470 | try extra_tombs.feed(arg); | |
| 477 | for (inputs) |input| { | |
| 478 | try extra_tombs.feed(input); | |
| 471 | 479 | } |
| 472 | 480 | return extra_tombs.finish(); |
| 473 | 481 | }, |
src/Module.zig+21-5| ... | ... | @@ -1370,6 +1370,14 @@ pub const Fn = struct { |
| 1370 | 1370 | /// ZIR instruction. |
| 1371 | 1371 | zir_body_inst: Zir.Inst.Index, |
| 1372 | 1372 | |
| 1373 | /// Prefer to use `getParamName` to access this because of the future improvement | |
| 1374 | /// we want to do mentioned in the TODO below. | |
| 1375 | /// Stored in gpa. | |
| 1376 | /// TODO: change param ZIR instructions to be embedded inside the function | |
| 1377 | /// ZIR instruction instead of before it, so that `zir_body_inst` can be used to | |
| 1378 | /// determine param names rather than redundantly storing them here. | |
| 1379 | param_names: []const [:0]const u8, | |
| 1380 | ||
| 1373 | 1381 | /// Relative to owner Decl. |
| 1374 | 1382 | lbrace_line: u32, |
| 1375 | 1383 | /// Relative to owner Decl. |
| ... | ... | @@ -1466,6 +1474,18 @@ pub const Fn = struct { |
| 1466 | 1474 | gpa.destroy(node); |
| 1467 | 1475 | it = next; |
| 1468 | 1476 | } |
| 1477 | ||
| 1478 | for (func.param_names) |param_name| { | |
| 1479 | gpa.free(param_name); | |
| 1480 | } | |
| 1481 | gpa.free(func.param_names); | |
| 1482 | } | |
| 1483 | ||
| 1484 | pub fn getParamName(func: Fn, index: u32) [:0]const u8 { | |
| 1485 | // TODO rework ZIR of parameters so that this function looks up | |
| 1486 | // param names in ZIR instead of redundantly saving them into Fn. | |
| 1487 | // const zir = func.owner_decl.getFileScope().zir; | |
| 1488 | return func.param_names[index]; | |
| 1469 | 1489 | } |
| 1470 | 1490 | }; |
| 1471 | 1491 | |
| ... | ... | @@ -4606,15 +4626,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem |
| 4606 | 4626 | runtime_param_index += 1; |
| 4607 | 4627 | continue; |
| 4608 | 4628 | } |
| 4609 | const ty_ref = try sema.addType(param_type); | |
| 4610 | 4629 | const arg_index = @intCast(u32, sema.air_instructions.len); |
| 4611 | 4630 | inner_block.instructions.appendAssumeCapacity(arg_index); |
| 4612 | 4631 | sema.air_instructions.appendAssumeCapacity(.{ |
| 4613 | 4632 | .tag = .arg, |
| 4614 | .data = .{ .ty_str = .{ | |
| 4615 | .ty = ty_ref, | |
| 4616 | .str = param.name, | |
| 4617 | } }, | |
| 4633 | .data = .{ .ty = param_type }, | |
| 4618 | 4634 | }); |
| 4619 | 4635 | sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index)); |
| 4620 | 4636 | total_param_index += 1; |
src/Sema.zig+60-20| ... | ... | @@ -134,6 +134,7 @@ pub const Block = struct { |
| 134 | 134 | /// `noreturn` means `anytype`. |
| 135 | 135 | ty: Type, |
| 136 | 136 | is_comptime: bool, |
| 137 | name: []const u8, | |
| 137 | 138 | }; |
| 138 | 139 | |
| 139 | 140 | /// This `Block` maps a block ZIR instruction to the corresponding |
| ... | ... | @@ -284,13 +285,10 @@ pub const Block = struct { |
| 284 | 285 | }); |
| 285 | 286 | } |
| 286 | 287 | |
| 287 | fn addArg(block: *Block, ty: Type, name: u32) error{OutOfMemory}!Air.Inst.Ref { | |
| 288 | fn addArg(block: *Block, ty: Type) error{OutOfMemory}!Air.Inst.Ref { | |
| 288 | 289 | return block.addInst(.{ |
| 289 | 290 | .tag = .arg, |
| 290 | .data = .{ .ty_str = .{ | |
| 291 | .ty = try block.sema.addType(ty), | |
| 292 | .str = name, | |
| 293 | } }, | |
| 291 | .data = .{ .ty = ty }, | |
| 294 | 292 | }); |
| 295 | 293 | } |
| 296 | 294 | |
| ... | ... | @@ -1126,7 +1124,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 1126 | 1124 | .frame_address => return sema.zirFrameAddress( block, extended), |
| 1127 | 1125 | .alloc => return sema.zirAllocExtended( block, extended), |
| 1128 | 1126 | .builtin_extern => return sema.zirBuiltinExtern( block, extended), |
| 1129 | .@"asm" => return sema.zirAsm( block, extended, inst), | |
| 1127 | .@"asm" => return sema.zirAsm( block, extended), | |
| 1130 | 1128 | .typeof_peer => return sema.zirTypeofPeer( block, extended), |
| 1131 | 1129 | .compile_log => return sema.zirCompileLog( block, extended), |
| 1132 | 1130 | .add_with_overflow => return sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| ... | ... | @@ -4645,7 +4643,7 @@ fn analyzeCall( |
| 4645 | 4643 | } else { |
| 4646 | 4644 | // We insert into the map an instruction which is runtime-known |
| 4647 | 4645 | // but has the type of the argument. |
| 4648 | const child_arg = try child_block.addArg(arg_ty, 0); | |
| 4646 | const child_arg = try child_block.addArg(arg_ty); | |
| 4649 | 4647 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); |
| 4650 | 4648 | } |
| 4651 | 4649 | } |
| ... | ... | @@ -5712,6 +5710,11 @@ fn funcCommon( |
| 5712 | 5710 | break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr; |
| 5713 | 5711 | } else null; |
| 5714 | 5712 | |
| 5713 | const param_names = try sema.gpa.alloc([:0]const u8, block.params.items.len); | |
| 5714 | for (param_names) |*param_name, i| { | |
| 5715 | param_name.* = try sema.gpa.dupeZ(u8, block.params.items[i].name); | |
| 5716 | } | |
| 5717 | ||
| 5715 | 5718 | const fn_payload = try sema.arena.create(Value.Payload.Function); |
| 5716 | 5719 | new_func.* = .{ |
| 5717 | 5720 | .state = anal_state, |
| ... | ... | @@ -5722,6 +5725,7 @@ fn funcCommon( |
| 5722 | 5725 | .rbrace_line = src_locs.rbrace_line, |
| 5723 | 5726 | .lbrace_column = @truncate(u16, src_locs.columns), |
| 5724 | 5727 | .rbrace_column = @truncate(u16, src_locs.columns >> 16), |
| 5728 | .param_names = param_names, | |
| 5725 | 5729 | }; |
| 5726 | 5730 | if (maybe_inferred_error_set_node) |node| { |
| 5727 | 5731 | new_func.inferred_error_sets.prepend(node); |
| ... | ... | @@ -5746,10 +5750,6 @@ fn zirParam( |
| 5746 | 5750 | const param_name = sema.code.nullTerminatedString(extra.data.name); |
| 5747 | 5751 | const body = sema.code.extra[extra.end..][0..extra.data.body_len]; |
| 5748 | 5752 | |
| 5749 | // TODO check if param_name shadows a Decl. This only needs to be done if | |
| 5750 | // usingnamespace is implemented. | |
| 5751 | _ = param_name; | |
| 5752 | ||
| 5753 | 5753 | // We could be in a generic function instantiation, or we could be evaluating a generic |
| 5754 | 5754 | // function without any comptime args provided. |
| 5755 | 5755 | const param_ty = param_ty: { |
| ... | ... | @@ -5776,6 +5776,7 @@ fn zirParam( |
| 5776 | 5776 | try block.params.append(sema.gpa, .{ |
| 5777 | 5777 | .ty = Type.initTag(.generic_poison), |
| 5778 | 5778 | .is_comptime = comptime_syntax, |
| 5779 | .name = param_name, | |
| 5779 | 5780 | }); |
| 5780 | 5781 | try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison); |
| 5781 | 5782 | return; |
| ... | ... | @@ -5801,6 +5802,7 @@ fn zirParam( |
| 5801 | 5802 | try block.params.append(sema.gpa, .{ |
| 5802 | 5803 | .ty = param_ty, |
| 5803 | 5804 | .is_comptime = is_comptime, |
| 5805 | .name = param_name, | |
| 5804 | 5806 | }); |
| 5805 | 5807 | const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); |
| 5806 | 5808 | try sema.inst_map.putNoClobber(sema.gpa, inst, result); |
| ... | ... | @@ -5816,10 +5818,6 @@ fn zirParamAnytype( |
| 5816 | 5818 | const src = inst_data.src(); |
| 5817 | 5819 | const param_name = inst_data.get(sema.code); |
| 5818 | 5820 | |
| 5819 | // TODO check if param_name shadows a Decl. This only needs to be done if | |
| 5820 | // usingnamespace is implemented. | |
| 5821 | _ = param_name; | |
| 5822 | ||
| 5823 | 5821 | if (sema.inst_map.get(inst)) |air_ref| { |
| 5824 | 5822 | const param_ty = sema.typeOf(air_ref); |
| 5825 | 5823 | if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) { |
| ... | ... | @@ -5831,6 +5829,7 @@ fn zirParamAnytype( |
| 5831 | 5829 | try block.params.append(sema.gpa, .{ |
| 5832 | 5830 | .ty = param_ty, |
| 5833 | 5831 | .is_comptime = false, |
| 5832 | .name = param_name, | |
| 5834 | 5833 | }); |
| 5835 | 5834 | return; |
| 5836 | 5835 | } |
| ... | ... | @@ -5840,6 +5839,7 @@ fn zirParamAnytype( |
| 5840 | 5839 | try block.params.append(sema.gpa, .{ |
| 5841 | 5840 | .ty = Type.initTag(.generic_poison), |
| 5842 | 5841 | .is_comptime = comptime_syntax, |
| 5842 | .name = param_name, | |
| 5843 | 5843 | }); |
| 5844 | 5844 | try sema.inst_map.put(sema.gpa, inst, .generic_poison); |
| 5845 | 5845 | } |
| ... | ... | @@ -9083,7 +9083,6 @@ fn zirAsm( |
| 9083 | 9083 | sema: *Sema, |
| 9084 | 9084 | block: *Block, |
| 9085 | 9085 | extended: Zir.Inst.Extended.InstData, |
| 9086 | inst: Zir.Inst.Index, | |
| 9087 | 9086 | ) CompileError!Air.Inst.Ref { |
| 9088 | 9087 | const tracy = trace(@src()); |
| 9089 | 9088 | defer tracy.end(); |
| ... | ... | @@ -9094,6 +9093,7 @@ fn zirAsm( |
| 9094 | 9093 | const outputs_len = @truncate(u5, extended.small); |
| 9095 | 9094 | const inputs_len = @truncate(u5, extended.small >> 5); |
| 9096 | 9095 | const clobbers_len = @truncate(u5, extended.small >> 10); |
| 9096 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 9097 | 9097 | |
| 9098 | 9098 | if (extra.data.asm_source == 0) { |
| 9099 | 9099 | // This can move to become an AstGen error after inline assembly improvements land |
| ... | ... | @@ -9107,6 +9107,7 @@ fn zirAsm( |
| 9107 | 9107 | |
| 9108 | 9108 | var extra_i = extra.end; |
| 9109 | 9109 | var output_type_bits = extra.data.output_type_bits; |
| 9110 | var needed_capacity: usize = @typeInfo(Air.Asm).Struct.fields.len + outputs_len + inputs_len; | |
| 9110 | 9111 | |
| 9111 | 9112 | const Output = struct { constraint: []const u8, ty: Type }; |
| 9112 | 9113 | const output: ?Output = if (outputs_len == 0) null else blk: { |
| ... | ... | @@ -9121,6 +9122,8 @@ fn zirAsm( |
| 9121 | 9122 | } |
| 9122 | 9123 | |
| 9123 | 9124 | const constraint = sema.code.nullTerminatedString(output.data.constraint); |
| 9125 | needed_capacity += constraint.len / 4 + 1; | |
| 9126 | ||
| 9124 | 9127 | break :blk Output{ |
| 9125 | 9128 | .constraint = constraint, |
| 9126 | 9129 | .ty = try sema.resolveType(block, ret_ty_src, output.data.operand), |
| ... | ... | @@ -9138,28 +9141,65 @@ fn zirAsm( |
| 9138 | 9141 | _ = name; // TODO: use the name |
| 9139 | 9142 | |
| 9140 | 9143 | arg.* = sema.resolveInst(input.data.operand); |
| 9141 | inputs[arg_i] = sema.code.nullTerminatedString(input.data.constraint); | |
| 9144 | const constraint = sema.code.nullTerminatedString(input.data.constraint); | |
| 9145 | needed_capacity += constraint.len / 4 + 1; | |
| 9146 | inputs[arg_i] = constraint; | |
| 9142 | 9147 | } |
| 9143 | 9148 | |
| 9144 | 9149 | const clobbers = try sema.arena.alloc([]const u8, clobbers_len); |
| 9145 | 9150 | for (clobbers) |*name| { |
| 9146 | 9151 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); |
| 9147 | 9152 | extra_i += 1; |
| 9153 | ||
| 9154 | needed_capacity += name.*.len / 4 + 1; | |
| 9148 | 9155 | } |
| 9149 | 9156 | |
| 9150 | try sema.requireRuntimeBlock(block, src); | |
| 9157 | const asm_source = sema.code.nullTerminatedString(extra.data.asm_source); | |
| 9158 | needed_capacity += (asm_source.len + 3) / 4; | |
| 9159 | ||
| 9151 | 9160 | const gpa = sema.gpa; |
| 9152 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Asm).Struct.fields.len + args.len); | |
| 9161 | try sema.requireRuntimeBlock(block, src); | |
| 9162 | try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); | |
| 9153 | 9163 | const asm_air = try block.addInst(.{ |
| 9154 | 9164 | .tag = .assembly, |
| 9155 | 9165 | .data = .{ .ty_pl = .{ |
| 9156 | 9166 | .ty = if (output) |o| try sema.addType(o.ty) else Air.Inst.Ref.void_type, |
| 9157 | 9167 | .payload = sema.addExtraAssumeCapacity(Air.Asm{ |
| 9158 | .zir_index = inst, | |
| 9168 | .source_len = @intCast(u32, asm_source.len), | |
| 9169 | .outputs_len = outputs_len, | |
| 9170 | .inputs_len = @intCast(u32, args.len), | |
| 9171 | .flags = (@as(u32, @boolToInt(is_volatile)) << 31) | @intCast(u32, clobbers.len), | |
| 9159 | 9172 | }), |
| 9160 | 9173 | } }, |
| 9161 | 9174 | }); |
| 9175 | if (output != null) { | |
| 9176 | // Indicate the output is the asm instruction return value. | |
| 9177 | sema.air_extra.appendAssumeCapacity(@enumToInt(Air.Inst.Ref.none)); | |
| 9178 | } | |
| 9162 | 9179 | sema.appendRefsAssumeCapacity(args); |
| 9180 | if (output) |o| { | |
| 9181 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | |
| 9182 | mem.copy(u8, buffer, o.constraint); | |
| 9183 | buffer[o.constraint.len] = 0; | |
| 9184 | sema.air_extra.items.len += o.constraint.len / 4 + 1; | |
| 9185 | } | |
| 9186 | for (inputs) |constraint| { | |
| 9187 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | |
| 9188 | mem.copy(u8, buffer, constraint); | |
| 9189 | buffer[constraint.len] = 0; | |
| 9190 | sema.air_extra.items.len += constraint.len / 4 + 1; | |
| 9191 | } | |
| 9192 | for (clobbers) |clobber| { | |
| 9193 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | |
| 9194 | mem.copy(u8, buffer, clobber); | |
| 9195 | buffer[clobber.len] = 0; | |
| 9196 | sema.air_extra.items.len += clobber.len / 4 + 1; | |
| 9197 | } | |
| 9198 | { | |
| 9199 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); | |
| 9200 | mem.copy(u8, buffer, asm_source); | |
| 9201 | sema.air_extra.items.len += (asm_source.len + 3) / 4; | |
| 9202 | } | |
| 9163 | 9203 | return asm_air; |
| 9164 | 9204 | } |
| 9165 | 9205 |
src/arch/aarch64/CodeGen.zig+63-70| ... | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | |
| 8 | 7 | const Mir = @import("Mir.zig"); |
| 9 | 8 | const Emit = @import("Emit.zig"); |
| 10 | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | ... | @@ -2021,36 +2020,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2021 | 2020 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 2022 | 2021 | } |
| 2023 | 2022 | |
| 2024 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { | |
| 2025 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; | |
| 2026 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; | |
| 2027 | const name = zir.nullTerminatedString(ty_str.str); | |
| 2028 | const name_with_null = name.ptr[0 .. name.len + 1]; | |
| 2029 | const ty = self.air.getRefType(ty_str.ty); | |
| 2030 | ||
| 2031 | switch (mcv) { | |
| 2032 | .register => |reg| { | |
| 2033 | switch (self.debug_output) { | |
| 2034 | .dwarf => |dbg_out| { | |
| 2035 | try dbg_out.dbg_info.ensureUnusedCapacity(3); | |
| 2036 | dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter); | |
| 2037 | dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc | |
| 2038 | 1, // ULEB128 dwarf expression length | |
| 2039 | reg.dwarfLocOp(), | |
| 2040 | }); | |
| 2041 | try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); | |
| 2042 | try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 | |
| 2043 | dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string | |
| 2044 | }, | |
| 2045 | .plan9 => {}, | |
| 2046 | .none => {}, | |
| 2047 | } | |
| 2048 | }, | |
| 2049 | .stack_offset => {}, | |
| 2050 | else => {}, | |
| 2051 | } | |
| 2052 | } | |
| 2053 | ||
| 2054 | 2023 | fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2055 | 2024 | const arg_index = self.arg_index; |
| 2056 | 2025 | self.arg_index += 1; |
| ... | ... | @@ -2866,40 +2835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 2866 | 2835 | } |
| 2867 | 2836 | |
| 2868 | 2837 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2869 | const air_datas = self.air.instructions.items(.data); | |
| 2870 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | |
| 2871 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | |
| 2872 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | |
| 2873 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | |
| 2874 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | |
| 2875 | const outputs_len = @truncate(u5, extended.small); | |
| 2876 | const args_len = @truncate(u5, extended.small >> 5); | |
| 2877 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 2878 | _ = clobbers_len; // TODO honor these | |
| 2879 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 2880 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); | |
| 2881 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); | |
| 2882 | ||
| 2883 | if (outputs_len > 1) { | |
| 2884 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 2885 | } | |
| 2886 | var extra_i: usize = zir_extra.end; | |
| 2887 | const output_constraint: ?[]const u8 = out: { | |
| 2888 | var i: usize = 0; | |
| 2889 | while (i < outputs_len) : (i += 1) { | |
| 2890 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 2891 | extra_i = output.end; | |
| 2892 | break :out zir.nullTerminatedString(output.data.constraint); | |
| 2893 | } | |
| 2894 | break :out null; | |
| 2895 | }; | |
| 2838 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 2839 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); | |
| 2840 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; | |
| 2841 | const clobbers_len = @truncate(u31, extra.data.flags); | |
| 2842 | var extra_i: usize = extra.end; | |
| 2843 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 2844 | extra_i += outputs.len; | |
| 2845 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 2846 | extra_i += inputs.len; | |
| 2896 | 2847 | |
| 2897 | 2848 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 2898 | 2849 | const result: MCValue = if (dead) .dead else result: { |
| 2899 | for (args) |arg| { | |
| 2900 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 2901 | extra_i = input.end; | |
| 2902 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 2850 | if (outputs.len > 1) { | |
| 2851 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 2852 | } | |
| 2853 | ||
| 2854 | const output_constraint: ?[]const u8 = for (outputs) |output| { | |
| 2855 | if (output != .none) { | |
| 2856 | return self.fail("TODO implement codegen for non-expr asm", .{}); | |
| 2857 | } | |
| 2858 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 2859 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2860 | // for the string, we still use the next u32 for the null terminator. | |
| 2861 | extra_i += constraint.len / 4 + 1; | |
| 2862 | ||
| 2863 | break constraint; | |
| 2864 | } else null; | |
| 2865 | ||
| 2866 | for (inputs) |input| { | |
| 2867 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 2868 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2869 | // for the string, we still use the next u32 for the null terminator. | |
| 2870 | extra_i += constraint.len / 4 + 1; | |
| 2903 | 2871 | |
| 2904 | 2872 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 2905 | 2873 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -2908,11 +2876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2908 | 2876 | const reg = parseRegName(reg_name) orelse |
| 2909 | 2877 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 2910 | 2878 | |
| 2911 | const arg_mcv = try self.resolveInst(arg); | |
| 2879 | const arg_mcv = try self.resolveInst(input); | |
| 2912 | 2880 | try self.register_manager.getReg(reg, null); |
| 2913 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | |
| 2881 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); | |
| 2882 | } | |
| 2883 | ||
| 2884 | { | |
| 2885 | var clobber_i: u32 = 0; | |
| 2886 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | |
| 2887 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 2888 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2889 | // for the string, we still use the next u32 for the null terminator. | |
| 2890 | extra_i += clobber.len / 4 + 1; | |
| 2891 | ||
| 2892 | // TODO honor these | |
| 2893 | } | |
| 2914 | 2894 | } |
| 2915 | 2895 | |
| 2896 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 2897 | ||
| 2916 | 2898 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 2917 | 2899 | _ = try self.addInst(.{ |
| 2918 | 2900 | .tag = .svc, |
| ... | ... | @@ -2939,18 +2921,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2939 | 2921 | break :result MCValue{ .none = {} }; |
| 2940 | 2922 | } |
| 2941 | 2923 | }; |
| 2942 | if (outputs.len + args.len <= Liveness.bpi - 1) { | |
| 2924 | ||
| 2925 | simple: { | |
| 2943 | 2926 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 2944 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | |
| 2945 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | |
| 2927 | var buf_index: usize = 0; | |
| 2928 | for (outputs) |output| { | |
| 2929 | if (output == .none) continue; | |
| 2930 | ||
| 2931 | if (buf_index >= buf.len) break :simple; | |
| 2932 | buf[buf_index] = output; | |
| 2933 | buf_index += 1; | |
| 2934 | } | |
| 2935 | if (buf_index + inputs.len > buf.len) break :simple; | |
| 2936 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | |
| 2946 | 2937 | return self.finishAir(inst, result, buf); |
| 2947 | 2938 | } |
| 2948 | var bt = try self.iterateBigTomb(inst, outputs.len + args.len); | |
| 2939 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); | |
| 2949 | 2940 | for (outputs) |output| { |
| 2941 | if (output == .none) continue; | |
| 2942 | ||
| 2950 | 2943 | bt.feed(output); |
| 2951 | 2944 | } |
| 2952 | for (args) |arg| { | |
| 2953 | bt.feed(arg); | |
| 2945 | for (inputs) |input| { | |
| 2946 | bt.feed(input); | |
| 2954 | 2947 | } |
| 2955 | 2948 | return bt.finishAir(result); |
| 2956 | 2949 | } |
src/arch/arm/CodeGen.zig+63-40| ... | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | |
| 8 | 7 | const Mir = @import("Mir.zig"); |
| 9 | 8 | const Emit = @import("Emit.zig"); |
| 10 | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | ... | @@ -3075,40 +3074,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 3075 | 3074 | } |
| 3076 | 3075 | |
| 3077 | 3076 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3078 | const air_datas = self.air.instructions.items(.data); | |
| 3079 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | |
| 3080 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | |
| 3081 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | |
| 3082 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | |
| 3083 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | |
| 3084 | const outputs_len = @truncate(u5, extended.small); | |
| 3085 | const args_len = @truncate(u5, extended.small >> 5); | |
| 3086 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 3087 | _ = clobbers_len; // TODO honor these | |
| 3088 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 3089 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); | |
| 3090 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); | |
| 3091 | ||
| 3092 | if (outputs_len > 1) { | |
| 3093 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 3094 | } | |
| 3095 | var extra_i: usize = zir_extra.end; | |
| 3096 | const output_constraint: ?[]const u8 = out: { | |
| 3097 | var i: usize = 0; | |
| 3098 | while (i < outputs_len) : (i += 1) { | |
| 3099 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 3100 | extra_i = output.end; | |
| 3101 | break :out zir.nullTerminatedString(output.data.constraint); | |
| 3102 | } | |
| 3103 | break :out null; | |
| 3104 | }; | |
| 3077 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 3078 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); | |
| 3079 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; | |
| 3080 | const clobbers_len = @truncate(u31, extra.data.flags); | |
| 3081 | var extra_i: usize = extra.end; | |
| 3082 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 3083 | extra_i += outputs.len; | |
| 3084 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 3085 | extra_i += inputs.len; | |
| 3105 | 3086 | |
| 3106 | 3087 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 3107 | 3088 | const result: MCValue = if (dead) .dead else result: { |
| 3108 | for (args) |arg| { | |
| 3109 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 3110 | extra_i = input.end; | |
| 3111 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 3089 | if (outputs.len > 1) { | |
| 3090 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 3091 | } | |
| 3092 | ||
| 3093 | const output_constraint: ?[]const u8 = for (outputs) |output| { | |
| 3094 | if (output != .none) { | |
| 3095 | return self.fail("TODO implement codegen for non-expr asm", .{}); | |
| 3096 | } | |
| 3097 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 3098 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 3099 | // for the string, we still use the next u32 for the null terminator. | |
| 3100 | extra_i += constraint.len / 4 + 1; | |
| 3101 | ||
| 3102 | break constraint; | |
| 3103 | } else null; | |
| 3104 | ||
| 3105 | for (inputs) |input| { | |
| 3106 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 3107 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 3108 | // for the string, we still use the next u32 for the null terminator. | |
| 3109 | extra_i += constraint.len / 4 + 1; | |
| 3112 | 3110 | |
| 3113 | 3111 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 3114 | 3112 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -3117,11 +3115,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3117 | 3115 | const reg = parseRegName(reg_name) orelse |
| 3118 | 3116 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 3119 | 3117 | |
| 3120 | const arg_mcv = try self.resolveInst(arg); | |
| 3118 | const arg_mcv = try self.resolveInst(input); | |
| 3121 | 3119 | try self.register_manager.getReg(reg, null); |
| 3122 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | |
| 3120 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); | |
| 3121 | } | |
| 3122 | ||
| 3123 | { | |
| 3124 | var clobber_i: u32 = 0; | |
| 3125 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | |
| 3126 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 3127 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 3128 | // for the string, we still use the next u32 for the null terminator. | |
| 3129 | extra_i += clobber.len / 4 + 1; | |
| 3130 | ||
| 3131 | // TODO honor these | |
| 3132 | } | |
| 3123 | 3133 | } |
| 3124 | 3134 | |
| 3135 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 3136 | ||
| 3125 | 3137 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 3126 | 3138 | _ = try self.addInst(.{ |
| 3127 | 3139 | .tag = .svc, |
| ... | ... | @@ -3144,18 +3156,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3144 | 3156 | break :result MCValue{ .none = {} }; |
| 3145 | 3157 | } |
| 3146 | 3158 | }; |
| 3147 | if (outputs.len + args.len <= Liveness.bpi - 1) { | |
| 3159 | ||
| 3160 | simple: { | |
| 3148 | 3161 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 3149 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | |
| 3150 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | |
| 3162 | var buf_index: usize = 0; | |
| 3163 | for (outputs) |output| { | |
| 3164 | if (output == .none) continue; | |
| 3165 | ||
| 3166 | if (buf_index >= buf.len) break :simple; | |
| 3167 | buf[buf_index] = output; | |
| 3168 | buf_index += 1; | |
| 3169 | } | |
| 3170 | if (buf_index + inputs.len > buf.len) break :simple; | |
| 3171 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | |
| 3151 | 3172 | return self.finishAir(inst, result, buf); |
| 3152 | 3173 | } |
| 3153 | var bt = try self.iterateBigTomb(inst, outputs.len + args.len); | |
| 3174 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); | |
| 3154 | 3175 | for (outputs) |output| { |
| 3176 | if (output == .none) continue; | |
| 3177 | ||
| 3155 | 3178 | bt.feed(output); |
| 3156 | 3179 | } |
| 3157 | for (args) |arg| { | |
| 3158 | bt.feed(arg); | |
| 3180 | for (inputs) |input| { | |
| 3181 | bt.feed(input); | |
| 3159 | 3182 | } |
| 3160 | 3183 | return bt.finishAir(result); |
| 3161 | 3184 | } |
src/arch/arm/Emit.zig+2-4| ... | ... | @@ -393,11 +393,9 @@ fn addDbgInfoTypeReloc(self: *Emit, ty: Type) !void { |
| 393 | 393 | fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { |
| 394 | 394 | const mcv = self.function.args[arg_index]; |
| 395 | 395 | |
| 396 | const ty_str = self.function.air.instructions.items(.data)[inst].ty_str; | |
| 397 | const zir = &self.function.mod_fn.owner_decl.getFileScope().zir; | |
| 398 | const name = zir.nullTerminatedString(ty_str.str); | |
| 396 | const ty = self.function.air.instructions.items(.data)[inst].ty; | |
| 397 | const name = self.function.mod_fn.getParamName(arg_index); | |
| 399 | 398 | const name_with_null = name.ptr[0 .. name.len + 1]; |
| 400 | const ty = self.function.air.getRefType(ty_str.ty); | |
| 401 | 399 | |
| 402 | 400 | switch (mcv) { |
| 403 | 401 | .register => |reg| { |
src/arch/riscv64/CodeGen.zig+66-46| ... | ... | @@ -4,7 +4,6 @@ const mem = std.mem; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | |
| 8 | 7 | const Mir = @import("Mir.zig"); |
| 9 | 8 | const Emit = @import("Emit.zig"); |
| 10 | 9 | const Liveness = @import("../../Liveness.zig"); |
| ... | ... | @@ -1354,12 +1353,10 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1354 | 1353 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 1355 | 1354 | } |
| 1356 | 1355 | |
| 1357 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { | |
| 1358 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; | |
| 1359 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; | |
| 1360 | const name = zir.nullTerminatedString(ty_str.str); | |
| 1356 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void { | |
| 1357 | const ty = self.air.instructions.items(.data)[inst].ty; | |
| 1358 | const name = self.mod_fn.getParamName(arg_index); | |
| 1361 | 1359 | const name_with_null = name.ptr[0 .. name.len + 1]; |
| 1362 | const ty = self.air.getRefType(ty_str.ty); | |
| 1363 | 1360 | |
| 1364 | 1361 | switch (mcv) { |
| 1365 | 1362 | .register => |reg| { |
| ... | ... | @@ -1402,7 +1399,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1402 | 1399 | // TODO support stack-only arguments |
| 1403 | 1400 | // TODO Copy registers to the stack |
| 1404 | 1401 | const mcv = result; |
| 1405 | try self.genArgDbgInfo(inst, mcv); | |
| 1402 | try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index)); | |
| 1406 | 1403 | |
| 1407 | 1404 | if (self.liveness.isUnused(inst)) |
| 1408 | 1405 | return self.finishAirBookkeeping(); |
| ... | ... | @@ -1838,40 +1835,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 1838 | 1835 | } |
| 1839 | 1836 | |
| 1840 | 1837 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1841 | const air_datas = self.air.instructions.items(.data); | |
| 1842 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | |
| 1843 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | |
| 1844 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | |
| 1845 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | |
| 1846 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | |
| 1847 | const outputs_len = @truncate(u5, extended.small); | |
| 1848 | const args_len = @truncate(u5, extended.small >> 5); | |
| 1849 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 1850 | _ = clobbers_len; // TODO honor these | |
| 1851 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 1852 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]); | |
| 1853 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]); | |
| 1854 | ||
| 1855 | if (outputs_len > 1) { | |
| 1856 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 1857 | } | |
| 1858 | var extra_i: usize = zir_extra.end; | |
| 1859 | const output_constraint: ?[]const u8 = out: { | |
| 1860 | var i: usize = 0; | |
| 1861 | while (i < outputs_len) : (i += 1) { | |
| 1862 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 1863 | extra_i = output.end; | |
| 1864 | break :out zir.nullTerminatedString(output.data.constraint); | |
| 1865 | } | |
| 1866 | break :out null; | |
| 1867 | }; | |
| 1838 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 1839 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); | |
| 1840 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; | |
| 1841 | const clobbers_len = @truncate(u31, extra.data.flags); | |
| 1842 | var extra_i: usize = extra.end; | |
| 1843 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 1844 | extra_i += outputs.len; | |
| 1845 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 1846 | extra_i += inputs.len; | |
| 1868 | 1847 | |
| 1869 | 1848 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 1870 | 1849 | const result: MCValue = if (dead) .dead else result: { |
| 1871 | for (args) |arg| { | |
| 1872 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 1873 | extra_i = input.end; | |
| 1874 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 1850 | if (outputs.len > 1) { | |
| 1851 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 1852 | } | |
| 1853 | ||
| 1854 | const output_constraint: ?[]const u8 = for (outputs) |output| { | |
| 1855 | if (output != .none) { | |
| 1856 | return self.fail("TODO implement codegen for non-expr asm", .{}); | |
| 1857 | } | |
| 1858 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 1859 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 1860 | // for the string, we still use the next u32 for the null terminator. | |
| 1861 | extra_i += constraint.len / 4 + 1; | |
| 1862 | ||
| 1863 | break constraint; | |
| 1864 | } else null; | |
| 1865 | ||
| 1866 | for (inputs) |input| { | |
| 1867 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 1868 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 1869 | // for the string, we still use the next u32 for the null terminator. | |
| 1870 | extra_i += constraint.len / 4 + 1; | |
| 1875 | 1871 | |
| 1876 | 1872 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 1877 | 1873 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -1880,11 +1876,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1880 | 1876 | const reg = parseRegName(reg_name) orelse |
| 1881 | 1877 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 1882 | 1878 | |
| 1883 | const arg_mcv = try self.resolveInst(arg); | |
| 1879 | const arg_mcv = try self.resolveInst(input); | |
| 1884 | 1880 | try self.register_manager.getReg(reg, null); |
| 1885 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | |
| 1881 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); | |
| 1886 | 1882 | } |
| 1887 | 1883 | |
| 1884 | { | |
| 1885 | var clobber_i: u32 = 0; | |
| 1886 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | |
| 1887 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 1888 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 1889 | // for the string, we still use the next u32 for the null terminator. | |
| 1890 | extra_i += clobber.len / 4 + 1; | |
| 1891 | ||
| 1892 | // TODO honor these | |
| 1893 | } | |
| 1894 | } | |
| 1895 | ||
| 1896 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 1897 | ||
| 1888 | 1898 | if (mem.eql(u8, asm_source, "ecall")) { |
| 1889 | 1899 | _ = try self.addInst(.{ |
| 1890 | 1900 | .tag = .ecall, |
| ... | ... | @@ -1906,18 +1916,28 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 1906 | 1916 | break :result MCValue{ .none = {} }; |
| 1907 | 1917 | } |
| 1908 | 1918 | }; |
| 1909 | if (outputs.len + args.len <= Liveness.bpi - 1) { | |
| 1919 | simple: { | |
| 1910 | 1920 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 1911 | std.mem.copy(Air.Inst.Ref, &buf, outputs); | |
| 1912 | std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args); | |
| 1921 | var buf_index: usize = 0; | |
| 1922 | for (outputs) |output| { | |
| 1923 | if (output == .none) continue; | |
| 1924 | ||
| 1925 | if (buf_index >= buf.len) break :simple; | |
| 1926 | buf[buf_index] = output; | |
| 1927 | buf_index += 1; | |
| 1928 | } | |
| 1929 | if (buf_index + inputs.len > buf.len) break :simple; | |
| 1930 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | |
| 1913 | 1931 | return self.finishAir(inst, result, buf); |
| 1914 | 1932 | } |
| 1915 | var bt = try self.iterateBigTomb(inst, outputs.len + args.len); | |
| 1933 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); | |
| 1916 | 1934 | for (outputs) |output| { |
| 1935 | if (output == .none) continue; | |
| 1936 | ||
| 1917 | 1937 | bt.feed(output); |
| 1918 | 1938 | } |
| 1919 | for (args) |arg| { | |
| 1920 | bt.feed(arg); | |
| 1939 | for (inputs) |input| { | |
| 1940 | bt.feed(input); | |
| 1921 | 1941 | } |
| 1922 | 1942 | return bt.finishAir(result); |
| 1923 | 1943 | } |
src/arch/x86_64/CodeGen.zig+69-43| ... | ... | @@ -26,7 +26,6 @@ const Target = std.Target; |
| 26 | 26 | const Type = @import("../../type.zig").Type; |
| 27 | 27 | const TypedValue = @import("../../TypedValue.zig"); |
| 28 | 28 | const Value = @import("../../value.zig").Value; |
| 29 | const Zir = @import("../../Zir.zig"); | |
| 30 | 29 | |
| 31 | 30 | const InnerError = error{ |
| 32 | 31 | OutOfMemory, |
| ... | ... | @@ -3435,41 +3434,39 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 3435 | 3434 | } |
| 3436 | 3435 | |
| 3437 | 3436 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3438 | const air_datas = self.air.instructions.items(.data); | |
| 3439 | const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | |
| 3440 | const zir = self.mod_fn.owner_decl.getFileScope().zir; | |
| 3441 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | |
| 3442 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | |
| 3443 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | |
| 3444 | const outputs_len = @truncate(u5, extended.small); | |
| 3445 | const args_len = @truncate(u5, extended.small >> 5); | |
| 3446 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 3447 | _ = clobbers_len; // TODO honor these | |
| 3448 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 3449 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..args_len]); | |
| 3450 | ||
| 3451 | if (outputs_len > 1) { | |
| 3452 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 3453 | } | |
| 3454 | var extra_i: usize = zir_extra.end; | |
| 3455 | const output_constraint: ?[]const u8 = out: { | |
| 3456 | var i: usize = 0; | |
| 3457 | while (i < outputs_len) : (i += 1) { | |
| 3458 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 3459 | extra_i = output.end; | |
| 3460 | break :out zir.nullTerminatedString(output.data.constraint); | |
| 3461 | } | |
| 3462 | break :out null; | |
| 3463 | }; | |
| 3437 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | |
| 3438 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); | |
| 3439 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; | |
| 3440 | const clobbers_len = @truncate(u31, extra.data.flags); | |
| 3441 | var extra_i: usize = extra.end; | |
| 3442 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 3443 | extra_i += outputs.len; | |
| 3444 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 3445 | extra_i += inputs.len; | |
| 3464 | 3446 | |
| 3465 | 3447 | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 3466 | const result: MCValue = if (dead) | |
| 3467 | .dead | |
| 3468 | else result: { | |
| 3469 | for (args) |arg| { | |
| 3470 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 3471 | extra_i = input.end; | |
| 3472 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 3448 | const result: MCValue = if (dead) .dead else result: { | |
| 3449 | if (outputs.len > 1) { | |
| 3450 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | |
| 3451 | } | |
| 3452 | ||
| 3453 | const output_constraint: ?[]const u8 = for (outputs) |output| { | |
| 3454 | if (output != .none) { | |
| 3455 | return self.fail("TODO implement codegen for non-expr asm", .{}); | |
| 3456 | } | |
| 3457 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 3458 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 3459 | // for the string, we still use the next u32 for the null terminator. | |
| 3460 | extra_i += constraint.len / 4 + 1; | |
| 3461 | ||
| 3462 | break constraint; | |
| 3463 | } else null; | |
| 3464 | ||
| 3465 | for (inputs) |input| { | |
| 3466 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 3467 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 3468 | // for the string, we still use the next u32 for the null terminator. | |
| 3469 | extra_i += constraint.len / 4 + 1; | |
| 3473 | 3470 | |
| 3474 | 3471 | if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { |
| 3475 | 3472 | return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -3478,11 +3475,25 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3478 | 3475 | const reg = parseRegName(reg_name) orelse |
| 3479 | 3476 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 3480 | 3477 | |
| 3481 | const arg_mcv = try self.resolveInst(arg); | |
| 3478 | const arg_mcv = try self.resolveInst(input); | |
| 3482 | 3479 | try self.register_manager.getReg(reg, null); |
| 3483 | try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv); | |
| 3480 | try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); | |
| 3481 | } | |
| 3482 | ||
| 3483 | { | |
| 3484 | var clobber_i: u32 = 0; | |
| 3485 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | |
| 3486 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 3487 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 3488 | // for the string, we still use the next u32 for the null terminator. | |
| 3489 | extra_i += clobber.len / 4 + 1; | |
| 3490 | ||
| 3491 | // TODO honor these | |
| 3492 | } | |
| 3484 | 3493 | } |
| 3485 | 3494 | |
| 3495 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 3496 | ||
| 3486 | 3497 | { |
| 3487 | 3498 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); |
| 3488 | 3499 | while (iter.next()) |ins| { |
| ... | ... | @@ -3549,14 +3560,29 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 3549 | 3560 | break :result MCValue{ .none = {} }; |
| 3550 | 3561 | } |
| 3551 | 3562 | }; |
| 3552 | if (args.len <= Liveness.bpi - 1) { | |
| 3563 | ||
| 3564 | simple: { | |
| 3553 | 3565 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| 3554 | std.mem.copy(Air.Inst.Ref, &buf, args); | |
| 3566 | var buf_index: usize = 0; | |
| 3567 | for (outputs) |output| { | |
| 3568 | if (output == .none) continue; | |
| 3569 | ||
| 3570 | if (buf_index >= buf.len) break :simple; | |
| 3571 | buf[buf_index] = output; | |
| 3572 | buf_index += 1; | |
| 3573 | } | |
| 3574 | if (buf_index + inputs.len > buf.len) break :simple; | |
| 3575 | std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); | |
| 3555 | 3576 | return self.finishAir(inst, result, buf); |
| 3556 | 3577 | } |
| 3557 | var bt = try self.iterateBigTomb(inst, args.len); | |
| 3558 | for (args) |arg| { | |
| 3559 | bt.feed(arg); | |
| 3578 | var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); | |
| 3579 | for (outputs) |output| { | |
| 3580 | if (output == .none) continue; | |
| 3581 | ||
| 3582 | bt.feed(output); | |
| 3583 | } | |
| 3584 | for (inputs) |input| { | |
| 3585 | bt.feed(input); | |
| 3560 | 3586 | } |
| 3561 | 3587 | return bt.finishAir(result); |
| 3562 | 3588 | } |
| ... | ... | @@ -3635,7 +3661,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3635 | 3661 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3636 | 3662 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 3637 | 3663 | }, |
| 3638 | else => return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}), | |
| 3664 | else => return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}), | |
| 3639 | 3665 | } |
| 3640 | 3666 | }, |
| 3641 | 3667 | .embedded_in_code => { |
| ... | ... | @@ -3643,7 +3669,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE |
| 3643 | 3669 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3644 | 3670 | return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg }); |
| 3645 | 3671 | } |
| 3646 | return self.fail("TODO implement args on stack for {} with abi size > 8", .{mcv}); | |
| 3672 | return self.fail("TODO implement inputs on stack for {} with abi size > 8", .{mcv}); | |
| 3647 | 3673 | }, |
| 3648 | 3674 | .memory, |
| 3649 | 3675 | .direct_load, |
src/arch/x86_64/Emit.zig+4-6| ... | ... | @@ -946,15 +946,13 @@ fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 946 | 946 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 947 | 947 | const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data; |
| 948 | 948 | const mcv = emit.mir.function.args[arg_dbg_info.arg_index]; |
| 949 | try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack); | |
| 949 | try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack, arg_dbg_info.arg_index); | |
| 950 | 950 | } |
| 951 | 951 | |
| 952 | fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32) !void { | |
| 953 | const ty_str = emit.mir.function.air.instructions.items(.data)[inst].ty_str; | |
| 954 | const zir = &emit.mir.function.mod_fn.owner_decl.getFileScope().zir; | |
| 955 | const name = zir.nullTerminatedString(ty_str.str); | |
| 952 | fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32, arg_index: u32) !void { | |
| 953 | const ty = emit.mir.function.air.instructions.items(.data)[inst].ty; | |
| 954 | const name = emit.mir.function.mod_fn.getParamName(arg_index); | |
| 956 | 955 | const name_with_null = name.ptr[0 .. name.len + 1]; |
| 957 | const ty = emit.mir.function.air.getRefType(ty_str.ty); | |
| 958 | 956 | |
| 959 | 957 | switch (mcv) { |
| 960 | 958 | .register => |reg| { |
src/codegen/c.zig+56-41| ... | ... | @@ -15,7 +15,6 @@ const Decl = Module.Decl; |
| 15 | 15 | const trace = @import("../tracy.zig").trace; |
| 16 | 16 | const LazySrcLoc = Module.LazySrcLoc; |
| 17 | 17 | const Air = @import("../Air.zig"); |
| 18 | const Zir = @import("../Zir.zig"); | |
| 19 | 18 | const Liveness = @import("../Liveness.zig"); |
| 20 | 19 | |
| 21 | 20 | const Mutability = enum { Const, Mut }; |
| ... | ... | @@ -2807,49 +2806,48 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2807 | 2806 | } |
| 2808 | 2807 | |
| 2809 | 2808 | fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2810 | const air_datas = f.air.instructions.items(.data); | |
| 2811 | const air_extra = f.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload); | |
| 2812 | const zir = f.object.dg.decl.getFileScope().zir; | |
| 2813 | const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended; | |
| 2814 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | |
| 2815 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | |
| 2816 | const outputs_len = @truncate(u5, extended.small); | |
| 2817 | const args_len = @truncate(u5, extended.small >> 5); | |
| 2818 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 2819 | _ = clobbers_len; // TODO honor these | |
| 2820 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 2821 | const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end..][0..outputs_len]); | |
| 2822 | const args = @bitCast([]const Air.Inst.Ref, f.air.extra[air_extra.end + outputs.len ..][0..args_len]); | |
| 2823 | ||
| 2824 | if (outputs_len > 1) { | |
| 2809 | const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; | |
| 2810 | const extra = f.air.extraData(Air.Asm, ty_pl.payload); | |
| 2811 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; | |
| 2812 | const clobbers_len = @truncate(u31, extra.data.flags); | |
| 2813 | var extra_i: usize = extra.end; | |
| 2814 | const outputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 2815 | extra_i += outputs.len; | |
| 2816 | const inputs = @bitCast([]const Air.Inst.Ref, f.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 2817 | extra_i += inputs.len; | |
| 2818 | ||
| 2819 | if (!is_volatile and f.liveness.isUnused(inst)) return CValue.none; | |
| 2820 | ||
| 2821 | if (outputs.len > 1) { | |
| 2825 | 2822 | return f.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 2826 | 2823 | } |
| 2827 | 2824 | |
| 2828 | if (f.liveness.isUnused(inst) and !is_volatile) | |
| 2829 | return CValue.none; | |
| 2830 | ||
| 2831 | var extra_i: usize = zir_extra.end; | |
| 2832 | const output_constraint: ?[]const u8 = out: { | |
| 2833 | var i: usize = 0; | |
| 2834 | while (i < outputs_len) : (i += 1) { | |
| 2835 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 2836 | extra_i = output.end; | |
| 2837 | break :out zir.nullTerminatedString(output.data.constraint); | |
| 2825 | const output_constraint: ?[]const u8 = for (outputs) |output| { | |
| 2826 | if (output != .none) { | |
| 2827 | return f.fail("TODO implement codegen for non-expr asm", .{}); | |
| 2838 | 2828 | } |
| 2839 | break :out null; | |
| 2840 | }; | |
| 2841 | const args_extra_begin = extra_i; | |
| 2829 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); | |
| 2830 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2831 | // for the string, we still use the next u32 for the null terminator. | |
| 2832 | extra_i += constraint.len / 4 + 1; | |
| 2833 | ||
| 2834 | break constraint; | |
| 2835 | } else null; | |
| 2842 | 2836 | |
| 2843 | 2837 | const writer = f.object.writer(); |
| 2844 | for (args) |arg| { | |
| 2845 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 2846 | extra_i = input.end; | |
| 2847 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 2838 | const inputs_extra_begin = extra_i; | |
| 2839 | ||
| 2840 | for (inputs) |input| { | |
| 2841 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); | |
| 2842 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2843 | // for the string, we still use the next u32 for the null terminator. | |
| 2844 | extra_i += constraint.len / 4 + 1; | |
| 2845 | ||
| 2848 | 2846 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 2849 | 2847 | const reg = constraint[1 .. constraint.len - 1]; |
| 2850 | const arg_c_value = try f.resolveInst(arg); | |
| 2848 | const arg_c_value = try f.resolveInst(input); | |
| 2851 | 2849 | try writer.writeAll("register "); |
| 2852 | try f.renderType(writer, f.air.typeOf(arg)); | |
| 2850 | try f.renderType(writer, f.air.typeOf(input)); | |
| 2853 | 2851 | |
| 2854 | 2852 | try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); |
| 2855 | 2853 | try f.writeCValue(writer, arg_c_value); |
| ... | ... | @@ -2858,21 +2856,38 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2858 | 2856 | return f.fail("TODO non-explicit inline asm regs", .{}); |
| 2859 | 2857 | } |
| 2860 | 2858 | } |
| 2859 | ||
| 2860 | { | |
| 2861 | var clobber_i: u32 = 0; | |
| 2862 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | |
| 2863 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); | |
| 2864 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2865 | // for the string, we still use the next u32 for the null terminator. | |
| 2866 | extra_i += clobber.len / 4 + 1; | |
| 2867 | ||
| 2868 | // TODO honor these | |
| 2869 | } | |
| 2870 | } | |
| 2871 | ||
| 2872 | const asm_source = std.mem.sliceAsBytes(f.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 2873 | ||
| 2861 | 2874 | const volatile_string: []const u8 = if (is_volatile) "volatile " else ""; |
| 2862 | 2875 | try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, asm_source }); |
| 2863 | 2876 | if (output_constraint) |_| { |
| 2864 | 2877 | return f.fail("TODO: CBE inline asm output", .{}); |
| 2865 | 2878 | } |
| 2866 | if (args.len > 0) { | |
| 2879 | if (inputs.len > 0) { | |
| 2867 | 2880 | if (output_constraint == null) { |
| 2868 | 2881 | try writer.writeAll(" :"); |
| 2869 | 2882 | } |
| 2870 | 2883 | try writer.writeAll(": "); |
| 2871 | extra_i = args_extra_begin; | |
| 2872 | for (args) |_, index| { | |
| 2873 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 2874 | extra_i = input.end; | |
| 2875 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 2884 | extra_i = inputs_extra_begin; | |
| 2885 | for (inputs) |_, index| { | |
| 2886 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(f.air.extra[extra_i..]), 0); | |
| 2887 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2888 | // for the string, we still use the next u32 for the null terminator. | |
| 2889 | extra_i += constraint.len / 4 + 1; | |
| 2890 | ||
| 2876 | 2891 | if (constraint[0] == '{' and constraint[constraint.len - 1] == '}') { |
| 2877 | 2892 | const reg = constraint[1 .. constraint.len - 1]; |
| 2878 | 2893 | if (index > 0) { |
src/codegen/llvm.zig+51-50| ... | ... | @@ -2,24 +2,21 @@ const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | const Compilation = @import("../Compilation.zig"); | |
| 6 | const llvm = @import("llvm/bindings.zig"); | |
| 7 | const link = @import("../link.zig"); | |
| 8 | 5 | const log = std.log.scoped(.codegen); |
| 9 | 6 | const math = std.math; |
| 10 | 7 | const native_endian = builtin.cpu.arch.endian(); |
| 11 | 8 | |
| 9 | const llvm = @import("llvm/bindings.zig"); | |
| 10 | const link = @import("../link.zig"); | |
| 11 | const Compilation = @import("../Compilation.zig"); | |
| 12 | 12 | const build_options = @import("build_options"); |
| 13 | 13 | const Module = @import("../Module.zig"); |
| 14 | 14 | const TypedValue = @import("../TypedValue.zig"); |
| 15 | const Zir = @import("../Zir.zig"); | |
| 16 | 15 | const Air = @import("../Air.zig"); |
| 17 | 16 | const Liveness = @import("../Liveness.zig"); |
| 18 | 17 | const target_util = @import("../target.zig"); |
| 19 | ||
| 20 | 18 | const Value = @import("../value.zig").Value; |
| 21 | 19 | const Type = @import("../type.zig").Type; |
| 22 | ||
| 23 | 20 | const LazySrcLoc = Module.LazySrcLoc; |
| 24 | 21 | |
| 25 | 22 | const Error = error{ OutOfMemory, CodegenFail }; |
| ... | ... | @@ -2895,33 +2892,21 @@ pub const FuncGen = struct { |
| 2895 | 2892 | // as stage1. |
| 2896 | 2893 | |
| 2897 | 2894 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2898 | const air_asm = self.air.extraData(Air.Asm, ty_pl.payload); | |
| 2899 | const zir = self.dg.decl.getFileScope().zir; | |
| 2900 | const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; | |
| 2901 | const is_volatile = @truncate(u1, extended.small >> 15) != 0; | |
| 2902 | if (!is_volatile and self.liveness.isUnused(inst)) { | |
| 2903 | return null; | |
| 2904 | } | |
| 2905 | const outputs_len = @truncate(u5, extended.small); | |
| 2906 | if (outputs_len > 1) { | |
| 2895 | const extra = self.air.extraData(Air.Asm, ty_pl.payload); | |
| 2896 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; | |
| 2897 | const clobbers_len = @truncate(u31, extra.data.flags); | |
| 2898 | var extra_i: usize = extra.end; | |
| 2899 | ||
| 2900 | if (!is_volatile and self.liveness.isUnused(inst)) return null; | |
| 2901 | ||
| 2902 | const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 2903 | extra_i += outputs.len; | |
| 2904 | const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 2905 | extra_i += inputs.len; | |
| 2906 | ||
| 2907 | if (outputs.len > 1) { | |
| 2907 | 2908 | return self.todo("implement llvm codegen for asm with more than 1 output", .{}); |
| 2908 | 2909 | } |
| 2909 | const args_len = @truncate(u5, extended.small >> 5); | |
| 2910 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 2911 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | |
| 2912 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | |
| 2913 | const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_asm.end..][0..args_len]); | |
| 2914 | ||
| 2915 | var extra_i: usize = zir_extra.end; | |
| 2916 | const output_constraint: ?[]const u8 = out: { | |
| 2917 | var i: usize = 0; | |
| 2918 | while (i < outputs_len) : (i += 1) { | |
| 2919 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 2920 | extra_i = output.end; | |
| 2921 | break :out zir.nullTerminatedString(output.data.constraint); | |
| 2922 | } | |
| 2923 | break :out null; | |
| 2924 | }; | |
| 2925 | 2910 | |
| 2926 | 2911 | var llvm_constraints: std.ArrayListUnmanaged(u8) = .{}; |
| 2927 | 2912 | defer llvm_constraints.deinit(self.gpa); |
| ... | ... | @@ -2930,14 +2915,21 @@ pub const FuncGen = struct { |
| 2930 | 2915 | defer arena_allocator.deinit(); |
| 2931 | 2916 | const arena = arena_allocator.allocator(); |
| 2932 | 2917 | |
| 2933 | const llvm_params_len = args.len; | |
| 2918 | const llvm_params_len = inputs.len; | |
| 2934 | 2919 | const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len); |
| 2935 | 2920 | const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len); |
| 2936 | ||
| 2937 | 2921 | var llvm_param_i: usize = 0; |
| 2938 | 2922 | var total_i: usize = 0; |
| 2939 | 2923 | |
| 2940 | if (output_constraint) |constraint| { | |
| 2924 | for (outputs) |output| { | |
| 2925 | if (output != .none) { | |
| 2926 | return self.todo("implement inline asm with non-returned output", .{}); | |
| 2927 | } | |
| 2928 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 2929 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2930 | // for the string, we still use the next u32 for the null terminator. | |
| 2931 | extra_i += constraint.len / 4 + 1; | |
| 2932 | ||
| 2941 | 2933 | try llvm_constraints.ensureUnusedCapacity(self.gpa, constraint.len + 1); |
| 2942 | 2934 | if (total_i != 0) { |
| 2943 | 2935 | llvm_constraints.appendAssumeCapacity(','); |
| ... | ... | @@ -2948,11 +2940,13 @@ pub const FuncGen = struct { |
| 2948 | 2940 | total_i += 1; |
| 2949 | 2941 | } |
| 2950 | 2942 | |
| 2951 | for (args) |arg| { | |
| 2952 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 2953 | extra_i = input.end; | |
| 2954 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 2955 | const arg_llvm_value = try self.resolveInst(arg); | |
| 2943 | for (inputs) |input| { | |
| 2944 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 2945 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2946 | // for the string, we still use the next u32 for the null terminator. | |
| 2947 | extra_i += constraint.len / 4 + 1; | |
| 2948 | ||
| 2949 | const arg_llvm_value = try self.resolveInst(input); | |
| 2956 | 2950 | |
| 2957 | 2951 | llvm_param_values[llvm_param_i] = arg_llvm_value; |
| 2958 | 2952 | llvm_param_types[llvm_param_i] = arg_llvm_value.typeOf(); |
| ... | ... | @@ -2967,19 +2961,26 @@ pub const FuncGen = struct { |
| 2967 | 2961 | total_i += 1; |
| 2968 | 2962 | } |
| 2969 | 2963 | |
| 2970 | const clobbers = zir.extra[extra_i..][0..clobbers_len]; | |
| 2971 | for (clobbers) |clobber_index| { | |
| 2972 | const clobber = zir.nullTerminatedString(clobber_index); | |
| 2973 | try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); | |
| 2974 | if (total_i != 0) { | |
| 2975 | llvm_constraints.appendAssumeCapacity(','); | |
| 2976 | } | |
| 2977 | llvm_constraints.appendSliceAssumeCapacity("~{"); | |
| 2978 | llvm_constraints.appendSliceAssumeCapacity(clobber); | |
| 2979 | llvm_constraints.appendSliceAssumeCapacity("}"); | |
| 2964 | { | |
| 2965 | var clobber_i: u32 = 0; | |
| 2966 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | |
| 2967 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); | |
| 2968 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 2969 | // for the string, we still use the next u32 for the null terminator. | |
| 2970 | extra_i += clobber.len / 4 + 1; | |
| 2971 | ||
| 2972 | try llvm_constraints.ensureUnusedCapacity(self.gpa, clobber.len + 4); | |
| 2973 | if (total_i != 0) { | |
| 2974 | llvm_constraints.appendAssumeCapacity(','); | |
| 2975 | } | |
| 2976 | llvm_constraints.appendSliceAssumeCapacity("~{"); | |
| 2977 | llvm_constraints.appendSliceAssumeCapacity(clobber); | |
| 2978 | llvm_constraints.appendSliceAssumeCapacity("}"); | |
| 2980 | 2979 | |
| 2981 | total_i += 1; | |
| 2980 | total_i += 1; | |
| 2981 | } | |
| 2982 | 2982 | } |
| 2983 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 2983 | 2984 | |
| 2984 | 2985 | const ret_ty = self.air.typeOfIndex(inst); |
| 2985 | 2986 | const ret_llvm_ty = try self.dg.llvmType(ret_ty); |
src/print_air.zig+54-48| ... | ... | @@ -4,11 +4,10 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin; |
| 4 | 4 | |
| 5 | 5 | const Module = @import("Module.zig"); |
| 6 | 6 | const Value = @import("value.zig").Value; |
| 7 | const Zir = @import("Zir.zig"); | |
| 8 | 7 | const Air = @import("Air.zig"); |
| 9 | 8 | const Liveness = @import("Liveness.zig"); |
| 10 | 9 | |
| 11 | pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void { | |
| 10 | pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void { | |
| 12 | 11 | const instruction_bytes = air.instructions.len * |
| 13 | 12 | // Here we don't use @sizeOf(Air.Inst.Data) because it would include |
| 14 | 13 | // the debug safety tag but we want to measure release size. |
| ... | ... | @@ -49,7 +48,6 @@ pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void { |
| 49 | 48 | .gpa = gpa, |
| 50 | 49 | .arena = arena.allocator(), |
| 51 | 50 | .air = air, |
| 52 | .zir = zir, | |
| 53 | 51 | .liveness = liveness, |
| 54 | 52 | .indent = 2, |
| 55 | 53 | }; |
| ... | ... | @@ -63,7 +61,6 @@ const Writer = struct { |
| 63 | 61 | gpa: Allocator, |
| 64 | 62 | arena: Allocator, |
| 65 | 63 | air: Air, |
| 66 | zir: Zir, | |
| 67 | 64 | liveness: Liveness, |
| 68 | 65 | indent: usize, |
| 69 | 66 | |
| ... | ... | @@ -100,8 +97,6 @@ const Writer = struct { |
| 100 | 97 | const tag = tags[inst]; |
| 101 | 98 | try s.print("= {s}(", .{@tagName(tags[inst])}); |
| 102 | 99 | switch (tag) { |
| 103 | .arg => try w.writeTyStr(s, inst), | |
| 104 | ||
| 105 | 100 | .add, |
| 106 | 101 | .addwrap, |
| 107 | 102 | .add_sat, |
| ... | ... | @@ -181,6 +176,7 @@ const Writer = struct { |
| 181 | 176 | .const_ty, |
| 182 | 177 | .alloc, |
| 183 | 178 | .ret_ptr, |
| 179 | .arg, | |
| 184 | 180 | => try w.writeTy(s, inst), |
| 185 | 181 | |
| 186 | 182 | .not, |
| ... | ... | @@ -259,12 +255,6 @@ const Writer = struct { |
| 259 | 255 | } |
| 260 | 256 | } |
| 261 | 257 | |
| 262 | fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | |
| 263 | const ty_str = w.air.instructions.items(.data)[inst].ty_str; | |
| 264 | const name = w.zir.nullTerminatedString(ty_str.str); | |
| 265 | try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), w.air.getRefType(ty_str.ty) }); | |
| 266 | } | |
| 267 | ||
| 268 | 258 | fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 269 | 259 | const bin_op = w.air.instructions.items(.data)[inst].bin_op; |
| 270 | 260 | try w.writeOperand(s, inst, 0, bin_op.lhs); |
| ... | ... | @@ -440,51 +430,67 @@ const Writer = struct { |
| 440 | 430 | |
| 441 | 431 | fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 442 | 432 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 443 | const air_asm = w.air.extraData(Air.Asm, ty_pl.payload); | |
| 444 | const zir = w.zir; | |
| 445 | const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; | |
| 446 | const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); | |
| 447 | const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); | |
| 448 | const outputs_len = @truncate(u5, extended.small); | |
| 449 | const args_len = @truncate(u5, extended.small >> 5); | |
| 450 | const clobbers_len = @truncate(u5, extended.small >> 10); | |
| 451 | const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]); | |
| 452 | ||
| 453 | var extra_i: usize = zir_extra.end; | |
| 454 | const output_constraint: ?[]const u8 = out: { | |
| 455 | var i: usize = 0; | |
| 456 | while (i < outputs_len) : (i += 1) { | |
| 457 | const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); | |
| 458 | extra_i = output.end; | |
| 459 | break :out zir.nullTerminatedString(output.data.constraint); | |
| 460 | } | |
| 461 | break :out null; | |
| 462 | }; | |
| 433 | const extra = w.air.extraData(Air.Asm, ty_pl.payload); | |
| 434 | const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; | |
| 435 | const clobbers_len = @truncate(u31, extra.data.flags); | |
| 436 | var extra_i: usize = extra.end; | |
| 437 | var op_index: usize = 0; | |
| 463 | 438 | |
| 464 | try s.print("\"{s}\"", .{asm_source}); | |
| 439 | const ret_ty = w.air.typeOfIndex(inst); | |
| 440 | try s.print("{}", .{ret_ty}); | |
| 465 | 441 | |
| 466 | if (output_constraint) |constraint| { | |
| 467 | const ret_ty = w.air.typeOfIndex(inst); | |
| 468 | try s.print(", {s} -> {}", .{ constraint, ret_ty }); | |
| 442 | if (is_volatile) { | |
| 443 | try s.writeAll(", volatile"); | |
| 469 | 444 | } |
| 470 | 445 | |
| 471 | for (args) |arg| { | |
| 472 | const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); | |
| 473 | extra_i = input.end; | |
| 474 | const constraint = zir.nullTerminatedString(input.data.constraint); | |
| 446 | const outputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.outputs_len]); | |
| 447 | extra_i += outputs.len; | |
| 448 | const inputs = @bitCast([]const Air.Inst.Ref, w.air.extra[extra_i..][0..extra.data.inputs_len]); | |
| 449 | extra_i += inputs.len; | |
| 450 | ||
| 451 | for (outputs) |output| { | |
| 452 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); | |
| 453 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 454 | // for the string, we still use the next u32 for the null terminator. | |
| 455 | extra_i += constraint.len / 4 + 1; | |
| 475 | 456 | |
| 476 | try s.print(", {s} = (", .{constraint}); | |
| 477 | try w.writeOperand(s, inst, 0, arg); | |
| 457 | if (output == .none) { | |
| 458 | try s.print(", -> {s}", .{constraint}); | |
| 459 | } else { | |
| 460 | try s.print(", out {s} = (", .{constraint}); | |
| 461 | try w.writeOperand(s, inst, op_index, output); | |
| 462 | op_index += 1; | |
| 463 | try s.writeByte(')'); | |
| 464 | } | |
| 465 | } | |
| 466 | ||
| 467 | for (inputs) |input| { | |
| 468 | const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); | |
| 469 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 470 | // for the string, we still use the next u32 for the null terminator. | |
| 471 | extra_i += constraint.len / 4 + 1; | |
| 472 | ||
| 473 | try s.print(", in {s} = (", .{constraint}); | |
| 474 | try w.writeOperand(s, inst, op_index, input); | |
| 475 | op_index += 1; | |
| 478 | 476 | try s.writeByte(')'); |
| 479 | 477 | } |
| 480 | 478 | |
| 481 | const clobbers = zir.extra[extra_i..][0..clobbers_len]; | |
| 482 | for (clobbers) |clobber_index| { | |
| 483 | const clobber = zir.nullTerminatedString(clobber_index); | |
| 484 | try s.writeAll(", ~{"); | |
| 485 | try s.writeAll(clobber); | |
| 486 | try s.writeAll("}"); | |
| 479 | { | |
| 480 | var clobber_i: u32 = 0; | |
| 481 | while (clobber_i < clobbers_len) : (clobber_i += 1) { | |
| 482 | const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(w.air.extra[extra_i..]), 0); | |
| 483 | // This equation accounts for the fact that even if we have exactly 4 bytes | |
| 484 | // for the string, we still use the next u32 for the null terminator. | |
| 485 | extra_i += clobber.len / 4 + 1; | |
| 486 | ||
| 487 | try s.writeAll(", ~{"); | |
| 488 | try s.writeAll(clobber); | |
| 489 | try s.writeAll("}"); | |
| 490 | } | |
| 487 | 491 | } |
| 492 | const asm_source = std.mem.sliceAsBytes(w.air.extra[extra_i..])[0..extra.data.source_len]; | |
| 493 | try s.print(", \"{s}\"", .{asm_source}); | |
| 488 | 494 | } |
| 489 | 495 | |
| 490 | 496 | fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |