| author | |
| committer | |
| log | e8236551abdb7bb74811e5e9dc9890cb2abbd269 |
| tree | a7cdaddfb3e7b5cd3f760b682e6ded775a68bfc5 |
| parent | 1c636e2564e2fc2e8e4b6b1edbc782592ee3d2d7 |
5 files changed, 62 insertions(+), 63 deletions(-)
src/air.zig-2| ... | ... | @@ -395,8 +395,6 @@ pub const Inst = struct { |
| 395 | 395 | |
| 396 | 396 | base: Inst, |
| 397 | 397 | body: Body, |
| 398 | /// This memory is reserved for codegen code to do whatever it needs to here. | |
| 399 | codegen: codegen.BlockData = .{}, | |
| 400 | 398 | |
| 401 | 399 | pub fn operandCount(self: *const Block) usize { |
| 402 | 400 | return 0; |
src/codegen.zig+40-42| ... | ... | @@ -22,37 +22,6 @@ const RegisterManager = @import("register_manager.zig").RegisterManager; |
| 22 | 22 | |
| 23 | 23 | const X8664Encoder = @import("codegen/x86_64.zig").Encoder; |
| 24 | 24 | |
| 25 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. | |
| 26 | pub const BlockData = struct { | |
| 27 | relocs: std.ArrayListUnmanaged(Reloc) = undefined, | |
| 28 | /// The first break instruction encounters `null` here and chooses a | |
| 29 | /// machine code value for the block result, populating this field. | |
| 30 | /// Following break instructions encounter that value and use it for | |
| 31 | /// the location to store their block results. | |
| 32 | mcv: AnyMCValue = undefined, | |
| 33 | }; | |
| 34 | ||
| 35 | /// Architecture-independent MCValue. Here, we have a type that is the same size as | |
| 36 | /// the architecture-specific MCValue. Next to the declaration of MCValue is a | |
| 37 | /// comptime assert that makes sure we guessed correctly about the size. This only | |
| 38 | /// exists so that we can bitcast an arch-independent field to and from the real MCValue. | |
| 39 | pub const AnyMCValue = extern struct { | |
| 40 | a: usize, | |
| 41 | b: u64, | |
| 42 | }; | |
| 43 | ||
| 44 | pub const Reloc = union(enum) { | |
| 45 | /// The value is an offset into the `Function` `code` from the beginning. | |
| 46 | /// To perform the reloc, write 32-bit signed little-endian integer | |
| 47 | /// which is a relative jump, based on the address following the reloc. | |
| 48 | rel32: usize, | |
| 49 | /// A branch in the ARM instruction set | |
| 50 | arm_branch: struct { | |
| 51 | pos: usize, | |
| 52 | cond: @import("codegen/arm.zig").Condition, | |
| 53 | }, | |
| 54 | }; | |
| 55 | ||
| 56 | 25 | pub const Result = union(enum) { |
| 57 | 26 | /// The `code` parameter passed to `generateSymbol` has the value appended. |
| 58 | 27 | appended: void, |
| ... | ... | @@ -317,6 +286,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 317 | 286 | /// across each runtime branch upon joining. |
| 318 | 287 | branch_stack: *std.ArrayList(Branch), |
| 319 | 288 | |
| 289 | blocks: std.AutoHashMapUnmanaged(*ir.Inst.Block, BlockData) = .{}, | |
| 290 | ||
| 320 | 291 | register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{}, |
| 321 | 292 | /// Maps offset to what is stored there. |
| 322 | 293 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| ... | ... | @@ -415,6 +386,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 415 | 386 | size: u32, |
| 416 | 387 | }; |
| 417 | 388 | |
| 389 | const BlockData = struct { | |
| 390 | relocs: std.ArrayListUnmanaged(Reloc), | |
| 391 | /// The first break instruction encounters `null` here and chooses a | |
| 392 | /// machine code value for the block result, populating this field. | |
| 393 | /// Following break instructions encounter that value and use it for | |
| 394 | /// the location to store their block results. | |
| 395 | mcv: MCValue, | |
| 396 | }; | |
| 397 | ||
| 398 | const Reloc = union(enum) { | |
| 399 | /// The value is an offset into the `Function` `code` from the beginning. | |
| 400 | /// To perform the reloc, write 32-bit signed little-endian integer | |
| 401 | /// which is a relative jump, based on the address following the reloc. | |
| 402 | rel32: usize, | |
| 403 | /// A branch in the ARM instruction set | |
| 404 | arm_branch: struct { | |
| 405 | pos: usize, | |
| 406 | cond: @import("codegen/arm.zig").Condition, | |
| 407 | }, | |
| 408 | }; | |
| 409 | ||
| 418 | 410 | const Self = @This(); |
| 419 | 411 | |
| 420 | 412 | fn generateSymbol( |
| ... | ... | @@ -463,6 +455,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 463 | 455 | .end_di_column = module_fn.rbrace_column, |
| 464 | 456 | }; |
| 465 | 457 | defer function.stack.deinit(bin_file.allocator); |
| 458 | defer function.blocks.deinit(bin_file.allocator); | |
| 466 | 459 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 467 | 460 | |
| 468 | 461 | var call_info = function.resolveCallingConventionValues(src_loc.lazy, fn_type) catch |err| switch (err) { |
| ... | ... | @@ -3025,7 +3018,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3025 | 3018 | } |
| 3026 | 3019 | |
| 3027 | 3020 | fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue { |
| 3028 | inst.codegen = .{ | |
| 3021 | try self.blocks.putNoClobber(self.gpa, inst, .{ | |
| 3029 | 3022 | // A block is a setup to be able to jump to the end. |
| 3030 | 3023 | .relocs = .{}, |
| 3031 | 3024 | // It also acts as a receptical for break operands. |
| ... | ... | @@ -3033,15 +3026,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3033 | 3026 | // break instruction will choose a MCValue for the block result and overwrite |
| 3034 | 3027 | // this field. Following break instructions will use that MCValue to put their |
| 3035 | 3028 | // block results. |
| 3036 | .mcv = @bitCast(AnyMCValue, MCValue{ .none = {} }), | |
| 3037 | }; | |
| 3038 | defer inst.codegen.relocs.deinit(self.gpa); | |
| 3029 | .mcv = MCValue{ .none = {} }, | |
| 3030 | }); | |
| 3031 | const block_data = &self.blocks.getEntry(inst).?.value; | |
| 3032 | defer block_data.relocs.deinit(self.gpa); | |
| 3039 | 3033 | |
| 3040 | 3034 | try self.genBody(inst.body); |
| 3041 | 3035 | |
| 3042 | for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc); | |
| 3036 | for (block_data.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc); | |
| 3043 | 3037 | |
| 3044 | return @bitCast(MCValue, inst.codegen.mcv); | |
| 3038 | return @bitCast(MCValue, block_data.mcv); | |
| 3045 | 3039 | } |
| 3046 | 3040 | |
| 3047 | 3041 | fn genSwitch(self: *Self, inst: *ir.Inst.SwitchBr) !MCValue { |
| ... | ... | @@ -3115,11 +3109,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3115 | 3109 | } |
| 3116 | 3110 | |
| 3117 | 3111 | fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue { |
| 3112 | const block_data = &self.blocks.getEntry(block).?.value; | |
| 3113 | ||
| 3118 | 3114 | if (operand.ty.hasCodeGenBits()) { |
| 3119 | 3115 | const operand_mcv = try self.resolveInst(operand); |
| 3120 | const block_mcv = @bitCast(MCValue, block.codegen.mcv); | |
| 3116 | const block_mcv = block_data.mcv; | |
| 3121 | 3117 | if (block_mcv == .none) { |
| 3122 | block.codegen.mcv = @bitCast(AnyMCValue, operand_mcv); | |
| 3118 | block_data.mcv = operand_mcv; | |
| 3123 | 3119 | } else { |
| 3124 | 3120 | try self.setRegOrMem(src, block.base.ty, block_mcv, operand_mcv); |
| 3125 | 3121 | } |
| ... | ... | @@ -3128,8 +3124,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3128 | 3124 | } |
| 3129 | 3125 | |
| 3130 | 3126 | fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue { |
| 3127 | const block_data = &self.blocks.getEntry(block).?.value; | |
| 3128 | ||
| 3131 | 3129 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 3132 | try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1); | |
| 3130 | try block_data.relocs.ensureCapacity(self.gpa, block_data.relocs.items.len + 1); | |
| 3133 | 3131 | |
| 3134 | 3132 | switch (arch) { |
| 3135 | 3133 | .i386, .x86_64 => { |
| ... | ... | @@ -3138,11 +3136,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3138 | 3136 | try self.code.resize(self.code.items.len + 5); |
| 3139 | 3137 | self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32 |
| 3140 | 3138 | // Leave the jump offset undefined |
| 3141 | block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 }); | |
| 3139 | block_data.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 }); | |
| 3142 | 3140 | }, |
| 3143 | 3141 | .arm, .armeb => { |
| 3144 | 3142 | try self.code.resize(self.code.items.len + 4); |
| 3145 | block.codegen.relocs.appendAssumeCapacity(.{ | |
| 3143 | block_data.relocs.appendAssumeCapacity(.{ | |
| 3146 | 3144 | .arm_branch = .{ |
| 3147 | 3145 | .pos = self.code.items.len - 4, |
| 3148 | 3146 | .cond = .al, |
src/codegen/c.zig+13-5| ... | ... | @@ -33,6 +33,11 @@ pub const CValue = union(enum) { |
| 33 | 33 | decl_ref: *Decl, |
| 34 | 34 | }; |
| 35 | 35 | |
| 36 | const BlockData = struct { | |
| 37 | block_id: usize, | |
| 38 | result: CValue, | |
| 39 | }; | |
| 40 | ||
| 36 | 41 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); |
| 37 | 42 | pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.hash, Type.eql, std.hash_map.default_max_load_percentage); |
| 38 | 43 | |
| ... | ... | @@ -83,6 +88,7 @@ pub const Object = struct { |
| 83 | 88 | gpa: *mem.Allocator, |
| 84 | 89 | code: std.ArrayList(u8), |
| 85 | 90 | value_map: CValueMap, |
| 91 | blocks: std.AutoHashMapUnmanaged(*ir.Inst.Block, BlockData) = .{}, | |
| 86 | 92 | next_arg_index: usize = 0, |
| 87 | 93 | next_local_index: usize = 0, |
| 88 | 94 | next_block_index: usize = 0, |
| ... | ... | @@ -939,8 +945,6 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue { |
| 939 | 945 | o.next_block_index += 1; |
| 940 | 946 | const writer = o.writer(); |
| 941 | 947 | |
| 942 | // store the block id in relocs.capacity as it is not used for anything else in the C backend. | |
| 943 | inst.codegen.relocs.capacity = block_id; | |
| 944 | 948 | const result = if (inst.base.ty.tag() != .void and !inst.base.isUnused()) blk: { |
| 945 | 949 | // allocate a location for the result |
| 946 | 950 | const local = try o.allocLocal(inst.base.ty, .Mut); |
| ... | ... | @@ -948,7 +952,11 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue { |
| 948 | 952 | break :blk local; |
| 949 | 953 | } else CValue{ .none = {} }; |
| 950 | 954 | |
| 951 | inst.codegen.mcv = @bitCast(@import("../codegen.zig").AnyMCValue, result); | |
| 955 | try o.blocks.putNoClobber(o.gpa, inst, .{ | |
| 956 | .block_id = block_id, | |
| 957 | .result = result, | |
| 958 | }); | |
| 959 | ||
| 952 | 960 | try genBody(o, inst.body); |
| 953 | 961 | try o.indent_writer.insertNewline(); |
| 954 | 962 | // label must be followed by an expression, add an empty one. |
| ... | ... | @@ -957,7 +965,7 @@ fn genBlock(o: *Object, inst: *Inst.Block) !CValue { |
| 957 | 965 | } |
| 958 | 966 | |
| 959 | 967 | fn genBr(o: *Object, inst: *Inst.Br) !CValue { |
| 960 | const result = @bitCast(CValue, inst.block.codegen.mcv); | |
| 968 | const result = o.blocks.get(inst.block).?.result; | |
| 961 | 969 | const writer = o.writer(); |
| 962 | 970 | |
| 963 | 971 | // If result is .none then the value of the block is unused. |
| ... | ... | @@ -973,7 +981,7 @@ fn genBr(o: *Object, inst: *Inst.Br) !CValue { |
| 973 | 981 | } |
| 974 | 982 | |
| 975 | 983 | fn genBrVoid(o: *Object, block: *Inst.Block) !CValue { |
| 976 | try o.writer().print("goto zig_block_{d};\n", .{block.codegen.relocs.capacity}); | |
| 984 | try o.writer().print("goto zig_block_{d};\n", .{o.blocks.get(block).?.block_id}); | |
| 977 | 985 | return CValue.none; |
| 978 | 986 | } |
| 979 | 987 |
src/codegen/wasm.zig+8-14| ... | ... | @@ -14,7 +14,6 @@ const Inst = ir.Inst; |
| 14 | 14 | const Type = @import("../type.zig").Type; |
| 15 | 15 | const Value = @import("../value.zig").Value; |
| 16 | 16 | const Compilation = @import("../Compilation.zig"); |
| 17 | const AnyMCValue = @import("../codegen.zig").AnyMCValue; | |
| 18 | 17 | const LazySrcLoc = Module.LazySrcLoc; |
| 19 | 18 | const link = @import("../link.zig"); |
| 20 | 19 | const TypedValue = @import("../TypedValue.zig"); |
| ... | ... | @@ -29,8 +28,6 @@ const WValue = union(enum) { |
| 29 | 28 | constant: *Inst, |
| 30 | 29 | /// Offset position in the list of bytecode instructions |
| 31 | 30 | code_offset: usize, |
| 32 | /// The label of the block, used by breaks to find its relative distance | |
| 33 | block_idx: u32, | |
| 34 | 31 | /// Used for variables that create multiple locals on the stack when allocated |
| 35 | 32 | /// such as structs and optionals. |
| 36 | 33 | multi_value: u32, |
| ... | ... | @@ -492,6 +489,8 @@ pub const Context = struct { |
| 492 | 489 | gpa: *mem.Allocator, |
| 493 | 490 | /// Table to save `WValue`'s generated by an `Inst` |
| 494 | 491 | values: ValueTable, |
| 492 | /// Mapping from *Inst.Block to block ids | |
| 493 | blocks: std.AutoArrayHashMapUnmanaged(*Inst.Block, u32) = .{}, | |
| 495 | 494 | /// `bytes` contains the wasm bytecode belonging to the 'code' section. |
| 496 | 495 | code: ArrayList(u8), |
| 497 | 496 | /// Contains the generated function type bytecode for the current function |
| ... | ... | @@ -521,6 +520,7 @@ pub const Context = struct { |
| 521 | 520 | |
| 522 | 521 | pub fn deinit(self: *Context) void { |
| 523 | 522 | self.values.deinit(self.gpa); |
| 523 | self.blocks.deinit(self.gpa); | |
| 524 | 524 | self.locals.deinit(self.gpa); |
| 525 | 525 | self.* = undefined; |
| 526 | 526 | } |
| ... | ... | @@ -590,7 +590,6 @@ pub const Context = struct { |
| 590 | 590 | fn emitWValue(self: *Context, val: WValue) InnerError!void { |
| 591 | 591 | const writer = self.code.writer(); |
| 592 | 592 | switch (val) { |
| 593 | .block_idx => unreachable, // block_idx cannot be referenced | |
| 594 | 593 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually |
| 595 | 594 | .none, .code_offset => {}, // no-op |
| 596 | 595 | .local => |idx| { |
| ... | ... | @@ -968,13 +967,9 @@ pub const Context = struct { |
| 968 | 967 | const block_ty = try self.genBlockType(block.base.src, block.base.ty); |
| 969 | 968 | |
| 970 | 969 | try self.startBlock(.block, block_ty, null); |
| 971 | block.codegen = .{ | |
| 972 | // we don't use relocs, so using `relocs` is illegal behaviour. | |
| 973 | .relocs = undefined, | |
| 974 | // Here we set the current block idx, so breaks know the depth to jump | |
| 975 | // to when breaking out. | |
| 976 | .mcv = @bitCast(AnyMCValue, WValue{ .block_idx = self.block_depth }), | |
| 977 | }; | |
| 970 | // Here we set the current block idx, so breaks know the depth to jump | |
| 971 | // to when breaking out. | |
| 972 | try self.blocks.putNoClobber(self.gpa, block, self.block_depth); | |
| 978 | 973 | try self.genBody(block.body); |
| 979 | 974 | try self.endBlock(); |
| 980 | 975 | |
| ... | ... | @@ -1091,10 +1086,9 @@ pub const Context = struct { |
| 1091 | 1086 | try self.emitWValue(operand); |
| 1092 | 1087 | } |
| 1093 | 1088 | |
| 1094 | // every block contains a `WValue` with its block index. | |
| 1089 | // We map every block to its block index. | |
| 1095 | 1090 | // We then determine how far we have to jump to it by substracting it from current block depth |
| 1096 | const wvalue = @bitCast(WValue, br.block.codegen.mcv); | |
| 1097 | const idx: u32 = self.block_depth - wvalue.block_idx; | |
| 1091 | const idx: u32 = self.block_depth - self.blocks.get(br.block).?; | |
| 1098 | 1092 | const writer = self.code.writer(); |
| 1099 | 1093 | try writer.writeByte(wasm.opcode(.br)); |
| 1100 | 1094 | try leb.writeULEB128(writer, idx); |
src/link/C.zig+1| ... | ... | @@ -125,6 +125,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void { |
| 125 | 125 | object.indent_writer = .{ .underlying_writer = object.code.writer() }; |
| 126 | 126 | defer { |
| 127 | 127 | object.value_map.deinit(); |
| 128 | object.blocks.deinit(module.gpa); | |
| 128 | 129 | object.code.deinit(); |
| 129 | 130 | object.dg.fwd_decl.deinit(); |
| 130 | 131 | var it = object.dg.typedefs.iterator(); |