From e13a182990c638bb69cd04e253ad6e0ecd734407 Mon Sep 17 00:00:00 2001 From: g-w1 <58830309+g-w1@users.noreply.github.com> Date: Mon, 21 Jun 2021 11:47:34 -0400 Subject: [PATCH] stage2 Sema: implement @intToPtr (#9144) Co-authored-by: Veikka Tuominen --- src/Sema.zig | 72 ++++++++++++++++++++++++++++++++++++++++++-- src/codegen.zig | 3 ++ src/type.zig | 4 +-- src/value.zig | 20 ++++++++++++ test/stage2/test.zig | 18 +++++++++++ 5 files changed, 112 insertions(+), 5 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 7c39e35bf8..62b9fcb7db 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5758,7 +5758,65 @@ fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr fn zirIntToPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); - return sema.mod.fail(&block.base, src, "TODO: Sema.zirIntToPtr", .{}); + + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const operand_res = try sema.resolveInst(extra.rhs); + const operand_coerced = try sema.coerce(block, Type.initTag(.usize), operand_res, operand_src); + + const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const type_res = try sema.resolveType(block, src, extra.lhs); + if (type_res.zigTypeTag() != .Pointer) + return sema.mod.fail(&block.base, type_src, "expected pointer, found '{}'", .{type_res}); + const ptr_align = type_res.ptrAlignment(sema.mod.getTarget()); + + const uncasted_operand = try sema.resolveInst(extra.rhs); + if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { + const addr = val.toUnsignedInt(); + if (!type_res.isAllowzeroPtr() and addr == 0) + return sema.mod.fail(&block.base, operand_src, "pointer type '{}' does not allow address zero", .{type_res}); + if (addr != 0 and addr % ptr_align != 0) + return sema.mod.fail(&block.base, operand_src, "pointer type '{}' requires aligned address", .{type_res}); + + const val_payload = try sema.arena.create(Value.Payload.U64); + val_payload.* = .{ + .base = .{ .tag = .int_u64 }, + .data = addr, + }; + return sema.mod.constInst(sema.arena, src, .{ + .ty = type_res, + .val = Value.initPayload(&val_payload.base), + }); + } + + try sema.requireRuntimeBlock(block, src); + if (block.wantSafety()) { + const zero = try sema.mod.constInst(sema.arena, src, .{ + .ty = Type.initTag(.u64), + .val = Value.initTag(.zero), + }); + if (!type_res.isAllowzeroPtr()) { + const is_non_zero = try block.addBinOp(src, Type.initTag(.bool), .cmp_neq, operand_coerced, zero); + try sema.addSafetyCheck(block, is_non_zero, .cast_to_null); + } + + if (ptr_align > 1) { + const val_payload = try sema.arena.create(Value.Payload.U64); + val_payload.* = .{ + .base = .{ .tag = .int_u64 }, + .data = ptr_align - 1, + }; + const align_minus_1 = try sema.mod.constInst(sema.arena, src, .{ + .ty = Type.initTag(.u64), + .val = Value.initPayload(&val_payload.base), + }); + const remainder = try block.addBinOp(src, Type.initTag(.u64), .bit_and, operand_coerced, align_minus_1); + const is_aligned = try block.addBinOp(src, Type.initTag(.bool), .cmp_eq, remainder, zero); + try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment); + } + } + return block.addUnOp(src, type_res, .bitcast, operand_coerced); } fn zirErrSetCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { @@ -6183,6 +6241,8 @@ pub const PanicId = enum { unreach, unwrap_null, unwrap_errunion, + cast_to_null, + incorrect_alignment, invalid_error_code, }; @@ -6805,10 +6865,13 @@ fn storePtr( if ((try sema.typeHasOnePossibleValue(block, src, elem_ty)) != null) return; - if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| { + if (try sema.resolvePossiblyUndefinedValue(block, src, ptr)) |ptr_val| blk: { const const_val = (try sema.resolvePossiblyUndefinedValue(block, src, value)) orelse return sema.mod.fail(&block.base, src, "cannot store runtime value in compile time variable", .{}); + if (ptr_val.tag() == .int_u64) + break :blk; // propogate it down to runtime + const comptime_alloc = ptr_val.castTag(.comptime_alloc).?; if (comptime_alloc.data.runtime_index < block.runtime_index) { if (block.runtime_cond) |cond_src| { @@ -6947,7 +7010,10 @@ fn analyzeLoad( .Pointer => ptr.ty.elemType(), else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}), }; - if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { + if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| blk: { + if (ptr_val.tag() == .int_u64) + break :blk; // do it at runtime + return sema.mod.constInst(sema.arena, src, .{ .ty = elem_ty, .val = try ptr_val.pointerDeref(sema.arena), diff --git a/src/codegen.zig b/src/codegen.zig index ca1c47629a..2b3c10b762 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -4169,6 +4169,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); } } + if (typed_value.val.tag() == .int_u64) { + return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + } return self.fail(src, "TODO codegen more kinds of const pointers", .{}); }, .Int => { diff --git a/src/type.zig b/src/type.zig index 410773b649..549b15f366 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1538,7 +1538,7 @@ pub const Type = extern union { .optional_single_const_pointer, .optional_single_mut_pointer, => { - if (self.elemType().hasCodeGenBits()) return 1; + if (!self.elemType().hasCodeGenBits()) return 1; return @divExact(target.cpu.arch.ptrBitWidth(), 8); }, @@ -1550,7 +1550,7 @@ pub const Type = extern union { .c_mut_pointer, .pointer, => { - if (self.elemType().hasCodeGenBits()) return 0; + if (!self.elemType().hasCodeGenBits()) return 0; return @divExact(target.cpu.arch.ptrBitWidth(), 8); }, diff --git a/src/value.zig b/src/value.zig index c358975667..696f3d3d88 100644 --- a/src/value.zig +++ b/src/value.zig @@ -979,6 +979,26 @@ pub const Value = extern union { }; } + /// Asserts the value is numeric + pub fn isZero(self: Value) bool { + return switch (self.tag()) { + .zero => true, + .one => false, + + .int_u64 => self.castTag(.int_u64).?.data == 0, + .int_i64 => self.castTag(.int_i64).?.data == 0, + + .float_16 => self.castTag(.float_16).?.data == 0, + .float_32 => self.castTag(.float_32).?.data == 0, + .float_64 => self.castTag(.float_64).?.data == 0, + .float_128 => self.castTag(.float_128).?.data == 0, + + .int_big_positive => self.castTag(.int_big_positive).?.asBigInt().eqZero(), + .int_big_negative => self.castTag(.int_big_negative).?.asBigInt().eqZero(), + else => unreachable, + }; + } + pub fn orderAgainstZero(lhs: Value) std.math.Order { return switch (lhs.tag()) { .zero, diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 8ee35be6d5..977f5f9ebf 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -1000,6 +1000,24 @@ pub fn addCases(ctx: *TestContext) !void { \\} , &[_][]const u8{":2:3: error: this is an error"}); + { + var case = ctx.exe("intToPtr", linux_x64); + case.addError( + \\pub fn main() void { + \\ _ = @intToPtr(*u8, 0); + \\} + , &[_][]const u8{ + ":2:24: error: pointer type '*u8' does not allow address zero", + }); + case.addError( + \\pub fn main() void { + \\ _ = @intToPtr(*u32, 2); + \\} + , &[_][]const u8{ + ":2:25: error: pointer type '*u32' requires aligned address", + }); + } + { var case = ctx.obj("variable shadowing", linux_x64); case.addError(