aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-02-28 17:42:59 +0100
committerGitHub <noreply@github.com>2022-02-28 17:42:59 +0100
commit331cc810ded2058f2e2767b0485eb18d888a45e5 (patch)
tree9e5ba37e5b06eaccf5ff36976955a32cda5ddfc4 /src
parent90059a12e0ffe433132450f9f43221a198a22106 (diff)
parent16f9774d2d6f358c97637e35609dfe0fc14cb501 (diff)
downloadzig-331cc810ded2058f2e2767b0485eb18d888a45e5.tar.gz
zig-331cc810ded2058f2e2767b0485eb18d888a45e5.zip
Merge pull request #11012 from ziglang/x64-union-tag
stage2,x64: basic (un)tagged unions
Diffstat (limited to 'src')
-rw-r--r--src/arch/x86_64/CodeGen.zig142
-rw-r--r--src/arch/x86_64/Emit.zig6
-rw-r--r--src/codegen.zig70
3 files changed, 169 insertions, 49 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 47d1e1f4d3..45d9767320 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -2098,17 +2098,72 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
- _ = bin_op;
- return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
+ const ptr_ty = self.air.typeOf(bin_op.lhs);
+ const union_ty = ptr_ty.childType();
+ const tag_ty = self.air.typeOf(bin_op.rhs);
+ const layout = union_ty.unionGetLayout(self.target.*);
+
+ if (layout.tag_size == 0) {
+ return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+
+ const ptr = try self.resolveInst(bin_op.lhs);
+ ptr.freezeIfRegister(&self.register_manager);
+ defer ptr.unfreezeIfRegister(&self.register_manager);
+
+ const tag = try self.resolveInst(bin_op.rhs);
+ tag.freezeIfRegister(&self.register_manager);
+ defer tag.unfreezeIfRegister(&self.register_manager);
+
+ const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: {
+ // TODO reusing the operand
+ const reg = try self.copyToTmpRegister(ptr_ty, ptr);
+ try self.genBinMathOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size });
+ break :blk MCValue{ .register = reg };
+ } else ptr;
+
+ try self.store(adjusted_ptr, tag, ptr_ty, tag_ty);
+
+ return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
- const result: MCValue = if (self.liveness.isUnused(inst))
- .dead
- else
- return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
- return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
+ }
+
+ const tag_ty = self.air.typeOfIndex(inst);
+ const union_ty = self.air.typeOf(ty_op.operand);
+ const layout = union_ty.unionGetLayout(self.target.*);
+
+ if (layout.tag_size == 0) {
+ return self.finishAir(inst, .none, .{ ty_op.operand, .none, .none });
+ }
+
+ // TODO reusing the operand
+ const operand = try self.resolveInst(ty_op.operand);
+ operand.freezeIfRegister(&self.register_manager);
+ defer operand.unfreezeIfRegister(&self.register_manager);
+
+ const tag_abi_size = tag_ty.abiSize(self.target.*);
+ const offset: i32 = if (layout.tag_align < layout.payload_align) @intCast(i32, layout.payload_size) else 0;
+ const dst_mcv: MCValue = blk: {
+ switch (operand) {
+ .stack_offset => |off| {
+ if (tag_abi_size <= 8) {
+ break :blk try self.copyToRegisterWithInstTracking(inst, tag_ty, .{
+ .stack_offset = off - offset,
+ });
+ }
+
+ return self.fail("TODO implement get_union_tag for ABI larger than 8 bytes and operand {}", .{operand});
+ },
+ else => return self.fail("TODO implement get_union_tag for {}", .{operand}),
+ }
+ };
+
+ return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
}
fn airClz(self: *Self, inst: Air.Inst.Index) !void {
@@ -2429,8 +2484,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
});
},
.stack_offset => {
- const tmp_reg = try self.copyToTmpRegister(value_ty, value);
- return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
+ if (abi_size <= 8) {
+ const tmp_reg = try self.copyToTmpRegister(value_ty, value);
+ return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
+ }
+
+ try self.genInlineMemcpy(0, value_ty, value, .{
+ .source_stack_base = .rbp,
+ .dest_stack_base = reg.to64(),
+ });
},
else => |other| {
return self.fail("TODO implement set pointee with {}", .{other});
@@ -3905,36 +3967,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
.dead, .unreach => unreachable,
.immediate => |imm| {
_ = try self.addInst(.{
- .tag = .@"test",
+ .tag = .xor,
.ops = (Mir.Ops{
.reg1 = registerAlias(cond_reg, abi_size),
}).encode(),
.data = .{ .imm = @intCast(u32, imm) },
});
- return self.addInst(.{
- .tag = .cond_jmp_eq_ne,
- .ops = (Mir.Ops{
- .flags = 0b00,
- }).encode(),
- .data = .{ .inst = undefined },
- });
},
.register => |reg| {
_ = try self.addInst(.{
- .tag = .@"test",
+ .tag = .xor,
.ops = (Mir.Ops{
.reg1 = registerAlias(cond_reg, abi_size),
.reg2 = registerAlias(reg, abi_size),
}).encode(),
.data = undefined,
});
- return self.addInst(.{
- .tag = .cond_jmp_eq_ne,
- .ops = (Mir.Ops{
- .flags = 0b00,
- }).encode(),
- .data = .{ .inst = undefined },
- });
},
.stack_offset => {
if (abi_size <= 8) {
@@ -3948,6 +3996,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
return self.fail("TODO implement switch mir when case is {}", .{case});
},
}
+
+ _ = try self.addInst(.{
+ .tag = .@"test",
+ .ops = (Mir.Ops{
+ .reg1 = registerAlias(cond_reg, abi_size),
+ .reg2 = registerAlias(cond_reg, abi_size),
+ }).encode(),
+ .data = undefined,
+ });
+ return self.addInst(.{
+ .tag = .cond_jmp_eq_ne,
+ .ops = (Mir.Ops{
+ .flags = 0b00,
+ }).encode(),
+ .data = .{ .inst = undefined },
+ });
},
.stack_offset => {
try self.spillCompareFlagsIfOccupied();
@@ -5408,24 +5472,14 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
}
switch (typed_value.ty.zigTypeTag()) {
- .Array => {
- return self.lowerUnnamedConst(typed_value);
- },
.Pointer => switch (typed_value.ty.ptrSize()) {
- .Slice => {
- return self.lowerUnnamedConst(typed_value);
- },
+ .Slice => {},
else => {
switch (typed_value.val.tag()) {
.int_u64 => {
return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
},
- .slice => {
- return self.lowerUnnamedConst(typed_value);
- },
- else => {
- return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()});
- },
+ else => {},
}
},
},
@@ -5434,10 +5488,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
if (info.bits <= ptr_bits and info.signedness == .signed) {
return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) };
}
- if (info.bits > ptr_bits or info.signedness == .signed) {
- return self.fail("TODO const int bigger than ptr and signed int", .{});
+ if (!(info.bits > ptr_bits or info.signedness == .signed)) {
+ return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
}
- return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
},
.Bool => {
return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
@@ -5457,7 +5510,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
} else if (typed_value.ty.abiSize(self.target.*) == 1) {
return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
}
- return self.fail("TODO non pointer optionals", .{});
},
.Enum => {
if (typed_value.val.castTag(.enum_field_index)) |field_index| {
@@ -5504,13 +5556,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
}
}
- return self.lowerUnnamedConst(typed_value);
- },
- .Struct => {
- return self.lowerUnnamedConst(typed_value);
},
- else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
+ else => {},
}
+
+ return self.lowerUnnamedConst(typed_value);
}
const CallMCValues = struct {
diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig
index fc43e61c17..f4d365dc4f 100644
--- a/src/arch/x86_64/Emit.zig
+++ b/src/arch/x86_64/Emit.zig
@@ -1859,6 +1859,9 @@ fn lowerToRmEnc(
switch (reg_or_mem) {
.register => |src_reg| {
const encoder = try Encoder.init(code, 4);
+ if (reg.size() == 16) {
+ encoder.prefix16BitMode();
+ }
encoder.rex(.{
.w = setRexWRegister(reg) or setRexWRegister(src_reg),
.r = reg.isExtended(),
@@ -1902,6 +1905,9 @@ fn lowerToMrEnc(
switch (reg_or_mem) {
.register => |dst_reg| {
const encoder = try Encoder.init(code, 3);
+ if (dst_reg.size() == 16) {
+ encoder.prefix16BitMode();
+ }
encoder.rex(.{
.w = setRexWRegister(dst_reg) or setRexWRegister(reg),
.r = reg.isExtended(),
diff --git a/src/codegen.zig b/src/codegen.zig
index 26a4478fb2..2484cb0e59 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -466,10 +466,74 @@ pub fn generateSymbol(
return Result{ .appended = {} };
},
.Union => {
- // TODO generateSymbol for unions
+ // TODO generate debug info for unions
const target = bin_file.options.target;
- const abi_size = try math.cast(usize, typed_value.ty.abiSize(target));
- try code.writer().writeByteNTimes(0xaa, abi_size);
+ const union_obj = typed_value.val.castTag(.@"union").?.data;
+ const layout = typed_value.ty.unionGetLayout(target);
+
+ if (layout.payload_size == 0) {
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+ .ty = typed_value.ty.unionTagType().?,
+ .val = union_obj.tag,
+ }, code, debug_output)) {
+ .appended => {},
+ .externally_managed => |external_slice| {
+ code.appendSliceAssumeCapacity(external_slice);
+ },
+ .fail => |em| return Result{ .fail = em },
+ }
+ }
+
+ // Check if we should store the tag first.
+ if (layout.tag_align >= layout.payload_align) {
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+ .ty = typed_value.ty.unionTagType().?,
+ .val = union_obj.tag,
+ }, code, debug_output)) {
+ .appended => {},
+ .externally_managed => |external_slice| {
+ code.appendSliceAssumeCapacity(external_slice);
+ },
+ .fail => |em| return Result{ .fail = em },
+ }
+ }
+
+ const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data;
+ const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag).?;
+ assert(union_ty.haveFieldTypes());
+ const field_ty = union_ty.fields.values()[field_index].ty;
+ if (!field_ty.hasRuntimeBits()) {
+ try code.writer().writeByteNTimes(0xaa, try math.cast(usize, layout.payload_size));
+ } else {
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+ .ty = field_ty,
+ .val = union_obj.val,
+ }, code, debug_output)) {
+ .appended => {},
+ .externally_managed => |external_slice| {
+ code.appendSliceAssumeCapacity(external_slice);
+ },
+ .fail => |em| return Result{ .fail = em },
+ }
+
+ const padding = try math.cast(usize, layout.payload_size - field_ty.abiSize(target));
+ if (padding > 0) {
+ try code.writer().writeByteNTimes(0, padding);
+ }
+ }
+
+ if (layout.tag_size > 0) {
+ switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
+ .ty = union_ty.tag_ty,
+ .val = union_obj.tag,
+ }, code, debug_output)) {
+ .appended => {},
+ .externally_managed => |external_slice| {
+ code.appendSliceAssumeCapacity(external_slice);
+ },
+ .fail => |em| return Result{ .fail = em },
+ }
+ }
return Result{ .appended = {} };
},