diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-02-24 15:08:23 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-02-24 15:08:23 -0700 |
| commit | 8e6c2b7a47c3c19269cf03075eaeddb7ee61c42d (patch) | |
| tree | 6fca0ca2a2929fe9fbb66dc960451d77cefb04bc /src | |
| parent | 38441b5eab3c9371f8412aa46a277f37fc026a79 (diff) | |
| parent | 8b9434871ea437840d25f073b945466359f402f9 (diff) | |
| download | zig-8e6c2b7a47c3c19269cf03075eaeddb7ee61c42d.tar.gz zig-8e6c2b7a47c3c19269cf03075eaeddb7ee61c42d.zip | |
Merge remote-tracking branch 'origin/master' into ast-memory-layout
Diffstat (limited to 'src')
| -rw-r--r-- | src/Cache.zig | 2 | ||||
| -rw-r--r-- | src/Compilation.zig | 6 | ||||
| -rw-r--r-- | src/codegen.zig | 119 | ||||
| -rw-r--r-- | src/codegen/wasm.zig | 30 | ||||
| -rw-r--r-- | src/link/Wasm.zig | 104 | ||||
| -rw-r--r-- | src/stage1.zig | 4 | ||||
| -rw-r--r-- | src/stage1/tokenizer.cpp | 4 |
7 files changed, 182 insertions, 87 deletions
diff --git a/src/Cache.zig b/src/Cache.zig index f5ffb34dbe..57ff9227fa 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -317,7 +317,7 @@ pub const Manifest = struct { cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; - std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; + _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; if (file_path.len == 0) { return error.InvalidFormat; diff --git a/src/Compilation.zig b/src/Compilation.zig index 227355de93..39e10becec 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -645,7 +645,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { }; const darwin_options: DarwinOptions = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: { - const opts: DarwinOptions = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: { + const opts: DarwinOptions = if (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()) inner: { // TODO Revisit this targeting versions lower than macOS 11 when LLVM 12 is out. // See https://github.com/ziglang/zig/issues/6996 const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11; @@ -1538,7 +1538,9 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 { } pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { - var progress: std.Progress = .{}; + // If the terminal is dumb, we dont want to show the user all the + // output. + var progress: std.Progress = .{ .dont_print_on_dumb = true }; var main_progress_node = try progress.start("", 0); defer main_progress_node.end(); if (self.color == .off) progress.terminal = null; diff --git a/src/codegen.zig b/src/codegen.zig index 095bb123ba..779366cc23 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -944,7 +944,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { /// Copies a value to a register without tracking the register. The register is not considered /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. - fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register { + fn copyToTmpRegister(self: *Self, src: usize, ty: Type, mcv: MCValue) !Register { const reg = self.findUnusedReg() orelse b: { // We'll take over the first register. Move the instruction that was previously // there to a stack allocation. @@ -961,7 +961,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { break :b reg; }; - try self.genSetReg(src, reg, mcv); + try self.genSetReg(src, ty, reg, mcv); return reg; } @@ -988,7 +988,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { break :b reg; }; - try self.genSetReg(reg_owner.src, reg, mcv); + try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); return MCValue{ .register = reg }; } @@ -1356,13 +1356,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // Load immediate into register if it doesn't fit // as an operand break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse - Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none); + Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none); }, .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), .stack_offset, .embedded_in_code, .memory, - => Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none), + => Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none), }; switch (op) { @@ -1448,7 +1448,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (src_mcv) { .immediate => |imm| { if (imm > math.maxInt(u31)) { - src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) }; + src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, Type.initTag(.u64), src_mcv) }; } }, else => {}, @@ -1479,7 +1479,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .register => |dst_reg| { switch (src_mcv) { .none => unreachable, - .undef => try self.genSetReg(src, dst_reg, .undef), + .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, .ptr_embedded_in_code => unreachable, @@ -1689,7 +1689,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (mc_arg) { .none => continue, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1758,7 +1758,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else unreachable; - try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.usize), .ra, .{ .memory = got_addr }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32()); } else if (func_value.castTag(.extern_fn)) |_| { return self.fail(inst.base.src, "TODO implement calling extern functions", .{}); @@ -1831,7 +1831,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1859,7 +1859,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else unreachable; - try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.usize), .lr, .{ .memory = got_addr }); // TODO: add Instruction.supportedOn // function for ARM @@ -1894,7 +1894,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1922,7 +1922,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else unreachable; - try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = got_addr }); writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); } else if (func_value.castTag(.extern_fn)) |_| { @@ -1945,7 +1945,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (mc_arg) { .none => continue, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1978,12 +1978,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); switch (arch) { .x86_64 => { - try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr }); // callq *%rax self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); }, .aarch64 => { - try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.u32), .x30, .{ .memory = got_addr }); // blr x30 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); }, @@ -2584,7 +2584,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "svc #0")) { @@ -2614,7 +2614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "svc #0")) { @@ -2646,7 +2646,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "ecall")) { @@ -2676,7 +2676,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "syscall")) { @@ -2738,7 +2738,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void { switch (loc) { .none => return, - .register => |reg| return self.genSetReg(src, reg, val), + .register => |reg| return self.genSetReg(src, ty, reg, val), .stack_offset => |off| return self.genSetStack(src, ty, off, val), .memory => { return self.fail(src, "TODO implement setRegOrMem for memory", .{}); @@ -2773,7 +2773,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); }, .immediate => { - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, .embedded_in_code => |code_offset| { @@ -2787,7 +2787,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { 1, 4 => { const offset = if (math.cast(u12, adj_off)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0); + } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); const str = switch (abi_size) { 1 => Instruction.strb, 4 => Instruction.str, @@ -2802,7 +2802,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { 2 => { const offset = if (adj_off <= math.maxInt(u8)) blk: { break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); + } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off })); writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{ .offset = offset, @@ -2819,7 +2819,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, }, @@ -2908,7 +2908,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, }, @@ -2936,7 +2936,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); }, .immediate => { - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, .embedded_in_code => |code_offset| { @@ -2951,7 +2951,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const offset = if (math.cast(i9, adj_off)) |imm| Instruction.LoadStoreOffset.imm_post_index(-imm) else |_| - Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); + Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off })); const rn: Register = switch (arch) { .aarch64, .aarch64_be => .x29, .aarch64_32 => .w29, @@ -2972,7 +2972,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, }, @@ -2980,7 +2980,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } - fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { + fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void { switch (arch) { .arm, .armeb => switch (mcv) { .dead => unreachable, @@ -2991,7 +2991,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (!self.wantSafety()) return; // The already existing value will do just fine. // Write the debug undefined value. - return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }); + return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }); }, .compare_flags_unsigned, .compare_flags_signed, @@ -3056,21 +3056,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(src, reg, .{ .immediate = addr }); + try self.genSetReg(src, ty, reg, .{ .immediate = addr }); writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); }, .stack_offset => |unadjusted_off| { // TODO: maybe addressing from sp instead of fp - // TODO: supply type information to genSetReg as we do to genSetStack - // const abi_size = ty.abiSize(self.target.*); - const abi_size = 4; + const abi_size = ty.abiSize(self.target.*); const adj_off = unadjusted_off + abi_size; switch (abi_size) { 1, 4 => { const offset = if (adj_off <= math.maxInt(u12)) blk: { break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); - } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0); + } else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); const ldr = switch (abi_size) { 1 => Instruction.ldrb, 4 => Instruction.ldr, @@ -3085,7 +3083,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { 2 => { const offset = if (adj_off <= math.maxInt(u8)) blk: { break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); + } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off })); writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{ .offset = offset, @@ -3107,8 +3105,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return; // The already existing value will do just fine. // Write the debug undefined value. switch (reg.size()) { - 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }), - 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), + 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }), + 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), else => unreachable, // unexpected register size } }, @@ -3221,7 +3219,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(src, reg, .{ .immediate = addr }); + try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32()); } }, @@ -3236,7 +3234,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (!self.wantSafety()) return; // The already existing value will do just fine. // Write the debug undefined value. - return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); + return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .immediate => |unsigned_x| { const x = @bitCast(i64, unsigned_x); @@ -3261,7 +3259,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(src, reg, .{ .immediate = addr }); + try self.genSetReg(src, ty, reg, .{ .immediate = addr }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32()); // LOAD imm=[i12 offset = 0], rs1 = @@ -3280,10 +3278,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return; // The already existing value will do just fine. // Write the debug undefined value. switch (reg.size()) { - 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }), - 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }), - 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }), - 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), + 8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }), + 16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }), + 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }), + 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), else => unreachable, } }, @@ -3497,7 +3495,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { assert(id3 != 4 and id3 != 5); // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. - try self.genSetReg(src, reg, MCValue{ .immediate = x }); + try self.genSetReg(src, ty, reg, MCValue{ .immediate = x }); // Now, the register contains the address of the value to load into it // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant. @@ -3596,7 +3594,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // This immediate is unsigned. const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); if (imm >= math.maxInt(U)) { - return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) }; + return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) }; } }, else => {}, @@ -3710,17 +3708,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { for (param_types) |ty, i| { switch (ty.zigTypeTag()) { .Bool, .Int => { - const param_size = @intCast(u32, ty.abiSize(self.target.*)); - if (next_int_reg >= c_abi_int_param_regs.len) { - result.args[i] = .{ .stack_offset = next_stack_offset }; - next_stack_offset += param_size; + if (!ty.hasCodeGenBits()) { + assert(cc != .C); + result.args[i] = .{ .none = {} }; } else { - const aliased_reg = registerAlias( - c_abi_int_param_regs[next_int_reg], - param_size, - ); - result.args[i] = .{ .register = aliased_reg }; - next_int_reg += 1; + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (next_int_reg >= c_abi_int_param_regs.len) { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_stack_offset += param_size; + } else { + const aliased_reg = registerAlias( + c_abi_int_param_regs[next_int_reg], + param_size, + ); + result.args[i] = .{ .register = aliased_reg }; + next_int_reg += 1; + } } }, else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index abc411b551..34e0b2f9b5 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -161,15 +161,19 @@ pub const Context = struct { pub fn gen(self: *Context) InnerError!void { assert(self.code.items.len == 0); try self.genFunctype(); - const writer = self.code.writer(); - - // Reserve space to write the size after generating the code as well as space for locals count - try self.code.resize(10); // Write instructions // TODO: check for and handle death of instructions const tv = self.decl.typed_value.most_recent.typed_value; - const mod_fn = tv.val.castTag(.function).?.data; + const mod_fn = blk: { + if (tv.val.castTag(.function)) |func| break :blk func.data; + if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions + return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()}); + }; + + // Reserve space to write the size after generating the code as well as space for locals count + try self.code.resize(10); + try self.genBody(mod_fn.body); // finally, write our local types at the 'offset' position @@ -189,6 +193,7 @@ pub const Context = struct { } } + const writer = self.code.writer(); try writer.writeByte(wasm.opcode(.end)); // Fill in the size of the generated code to the reserved space at the @@ -239,9 +244,16 @@ pub const Context = struct { fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue { const func_inst = inst.func.castTag(.constant).?; - const func = func_inst.val.castTag(.function).?.data; - const target = func.owner_decl; - const target_ty = target.typed_value.most_recent.typed_value.ty; + const func_val = inst.func.value().?; + + const target = blk: { + if (func_val.castTag(.function)) |func| { + break :blk func.data.owner_decl; + } else if (func_val.castTag(.extern_fn)) |ext_fn| { + break :blk ext_fn.data; + } + return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()}); + }; for (inst.args) |arg| { const arg_val = self.resolveInst(arg); @@ -495,7 +507,7 @@ pub const Context = struct { } fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue { - // of operand has codegen bits we should break with a value + // if operand has codegen bits we should break with a value if (br.operand.ty.hasCodeGenBits()) { const operand = self.resolveInst(br.operand); try self.emitWValue(operand); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index cafd17cd2c..e0e10ad88d 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -33,9 +33,18 @@ base: link.File, /// List of all function Decls to be written to the output file. The index of /// each Decl in this list at the time of writing the binary is used as the -/// function index. +/// function index. In the event where ext_funcs' size is not 0, the index of +/// each function is added on top of the ext_funcs' length. /// TODO: can/should we access some data structure in Module directly? funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, +/// List of all extern function Decls to be written to the `import` section of the +/// wasm binary. The positin in the list defines the function index +ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, +/// When importing objects from the host environment, a name must be supplied. +/// LLVM uses "env" by default when none is given. This would be a good default for Zig +/// to support existing code. +/// TODO: Allow setting this through a flag? +host_name: []const u8 = "env", pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { assert(options.object_format == .wasm); @@ -76,7 +85,13 @@ pub fn deinit(self: *Wasm) void { decl.fn_link.wasm.?.code.deinit(self.base.allocator); decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); } + for (self.ext_funcs.items) |decl| { + decl.fn_link.wasm.?.functype.deinit(self.base.allocator); + decl.fn_link.wasm.?.code.deinit(self.base.allocator); + decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); + } self.funcs.deinit(self.base.allocator); + self.ext_funcs.deinit(self.base.allocator); } // Generate code for the Decl, storing it in memory to be later written to @@ -85,8 +100,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { const typed_value = decl.typed_value.most_recent.typed_value; if (typed_value.ty.zigTypeTag() != .Fn) return error.TODOImplementNonFnDeclsForWasm; - if (typed_value.val.tag() == .extern_fn) - return error.TODOImplementExternFnDeclsForWasm; if (decl.fn_link.wasm) |*fn_data| { fn_data.functype.items.len = 0; @@ -94,7 +107,12 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { fn_data.idx_refs.items.len = 0; } else { decl.fn_link.wasm = .{}; - try self.funcs.append(self.base.allocator, decl); + // dependent on function type, appends it to the correct list + switch (decl.typed_value.most_recent.typed_value.val.tag()) { + .function => try self.funcs.append(self.base.allocator, decl), + .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), + else => return error.TODOImplementNonFnDeclsForWasm, + } } const fn_data = &decl.fn_link.wasm.?; @@ -143,7 +161,12 @@ pub fn updateDeclExports( pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { // TODO: remove this assert when non-function Decls are implemented assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); - _ = self.funcs.swapRemove(self.getFuncidx(decl).?); + const func_idx = self.getFuncidx(decl).?; + switch (decl.typed_value.most_recent.typed_value.val.tag()) { + .function => _ = self.funcs.swapRemove(func_idx), + .extern_fn => _ = self.ext_funcs.swapRemove(func_idx), + else => unreachable, + } decl.fn_link.wasm.?.functype.deinit(self.base.allocator); decl.fn_link.wasm.?.code.deinit(self.base.allocator); decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); @@ -172,15 +195,46 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { // Type section { const header_offset = try reserveVecSectionHeader(file); - for (self.funcs.items) |decl| { - try file.writeAll(decl.fn_link.wasm.?.functype.items); - } + + // extern functions are defined in the wasm binary first through the `import` + // section, so define their func types first + for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); + for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); + try writeVecSectionHeader( file, header_offset, .type, @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.funcs.items.len), + @intCast(u32, self.ext_funcs.items.len + self.funcs.items.len), + ); + } + + // Import section + { + // TODO: implement non-functions imports + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + for (self.ext_funcs.items) |decl, typeidx| { + try leb.writeULEB128(writer, @intCast(u32, self.host_name.len)); + try writer.writeAll(self.host_name); + + // wasm requires the length of the import name with no null-termination + const decl_len = mem.len(decl.name); + try leb.writeULEB128(writer, @intCast(u32, decl_len)); + try writer.writeAll(decl.name[0..decl_len]); + + // emit kind and the function type + try writer.writeByte(wasm.externalKind(.function)); + try leb.writeULEB128(writer, @intCast(u32, typeidx)); + } + + try writeVecSectionHeader( + file, + header_offset, + .import, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, self.ext_funcs.items.len), ); } @@ -188,7 +242,11 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { { const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); - for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx)); + for (self.funcs.items) |_, typeidx| { + const func_idx = @intCast(u32, self.getFuncIdxOffset() + typeidx); + try leb.writeULEB128(writer, func_idx); + } + try writeVecSectionHeader( file, header_offset, @@ -212,7 +270,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { .Fn => { // Type of the export - try writer.writeByte(0x00); + try writer.writeByte(wasm.externalKind(.function)); // Exported function index try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?); }, @@ -523,13 +581,31 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { } /// Get the current index of a given Decl in the function list -/// TODO: we could maintain a hash map to potentially make this +/// This will correctly provide the index, regardless whether the function is extern or not +/// TODO: we could maintain a hash map to potentially make this simpler fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { - return for (self.funcs.items) |func, idx| { - if (func == decl) break @intCast(u32, idx); + var offset: u32 = 0; + const slice = switch (decl.typed_value.most_recent.typed_value.val.tag()) { + .function => blk: { + // when the target is a regular function, we have to calculate + // the offset of where the index starts + offset += self.getFuncIdxOffset(); + break :blk self.funcs.items; + }, + .extern_fn => self.ext_funcs.items, + else => return null, + }; + return for (slice) |func, idx| { + if (func == decl) break @intCast(u32, offset + idx); } else null; } +/// Based on the size of `ext_funcs` returns the +/// offset of the function indices +fn getFuncIdxOffset(self: Wasm) u32 { + return @intCast(u32, self.ext_funcs.items.len); +} + fn reserveVecSectionHeader(file: fs.File) !u64 { // section id + fixed leb contents size + fixed leb vector length const header_size = 1 + 5 + 5; diff --git a/src/stage1.zig b/src/stage1.zig index 8ab3b1d94d..1b7eadd1a8 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -278,7 +278,9 @@ export fn stage2_attach_segfault_handler() void { // ABI warning export fn stage2_progress_create() *std.Progress { const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); - ptr.* = std.Progress{}; + // If the terminal is dumb, we dont want to show the user all the + // output. + ptr.* = std.Progress{ .dont_print_on_dumb = true }; return ptr; } diff --git a/src/stage1/tokenizer.cpp b/src/stage1/tokenizer.cpp index 1d25bca17b..623169a313 100644 --- a/src/stage1/tokenizer.cpp +++ b/src/stage1/tokenizer.cpp @@ -1447,7 +1447,7 @@ void tokenize(Buf *buf, Tokenization *out) { tokenize_error(&t, "unterminated string"); break; } else if (t.cur_tok->id == TokenIdCharLiteral) { - tokenize_error(&t, "unterminated character literal"); + tokenize_error(&t, "unterminated Unicode code point literal"); break; } else { zig_unreachable(); @@ -1456,7 +1456,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateCharLiteral: case TokenizeStateCharLiteralEnd: case TokenizeStateCharLiteralUnicode: - tokenize_error(&t, "unterminated character literal"); + tokenize_error(&t, "unterminated Unicode code point literal"); break; case TokenizeStateSymbol: case TokenizeStateZero: |
