stage2: implement more C pointer Sema and comptime ptr arith

This commit is contained in:
Andrew Kelley 2021-10-23 19:46:48 -07:00
parent ee98d87008
commit 22b4c9e1a9
6 changed files with 113 additions and 62 deletions

View File

@ -7983,12 +7983,25 @@ fn analyzePtrArithmetic(
const runtime_src = rs: {
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
if (try sema.resolveDefinedValue(block, offset_src, offset)) |offset_val| {
const ptr_ty = sema.typeOf(ptr);
const offset_int = offset_val.toUnsignedInt();
const new_ptr_ty = ptr_ty; // TODO modify alignment
if (ptr_val.getUnsignedInt()) |addr| {
const target = sema.mod.getTarget();
const elem_ty = ptr_ty.childType();
const elem_size = elem_ty.abiSize(target);
const new_addr = switch (air_tag) {
.ptr_add => addr + elem_size * offset_int,
.ptr_sub => addr - elem_size * offset_int,
else => unreachable,
};
const new_ptr_val = try Value.Tag.int_u64.create(sema.arena, new_addr);
return sema.addConstant(new_ptr_ty, new_ptr_val);
}
if (air_tag == .ptr_sub) {
return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
}
const offset_int = offset_val.toUnsignedInt();
const new_ptr_val = try ptr_val.elemPtr(sema.arena, offset_int);
const new_ptr_ty = sema.typeOf(ptr);
return sema.addConstant(new_ptr_ty, new_ptr_val);
} else break :rs offset_src;
} else break :rs ptr_src;
@ -11979,6 +11992,8 @@ fn coerce(
return sema.wrapOptional(block, dest_ty, intermediate, inst_src);
},
.Pointer => {
const dest_info = dest_ty.ptrInfo().data;
// Function body to function pointer.
if (inst_ty.zigTypeTag() == .Fn) {
const fn_val = try sema.resolveConstValue(block, inst_src, inst);
@ -11989,16 +12004,16 @@ fn coerce(
// *T to *[1]T
single_item: {
if (!dest_ty.isSinglePointer()) break :single_item;
if (dest_info.size != .One) break :single_item;
if (!inst_ty.isSinglePointer()) break :single_item;
const ptr_elem_ty = inst_ty.childType();
const array_ty = dest_ty.childType();
const array_ty = dest_info.pointee_type;
if (array_ty.zigTypeTag() != .Array) break :single_item;
const array_elem_ty = array_ty.childType();
const dest_is_mut = !dest_ty.isConstPtr();
const dest_is_mut = dest_info.mutable;
if (inst_ty.isConstPtr() and dest_is_mut) break :single_item;
if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :single_item;
if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :single_item;
if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :single_item;
if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :single_item;
switch (coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
.ok => {},
.no_match => break :single_item,
@ -12012,18 +12027,18 @@ fn coerce(
const array_ty = inst_ty.childType();
if (array_ty.zigTypeTag() != .Array) break :src_array_ptr;
const array_elem_type = array_ty.childType();
const dest_is_mut = !dest_ty.isConstPtr();
const dest_is_mut = dest_info.mutable;
if (inst_ty.isConstPtr() and dest_is_mut) break :src_array_ptr;
if (inst_ty.isVolatilePtr() and !dest_ty.isVolatilePtr()) break :src_array_ptr;
if (inst_ty.ptrAddressSpace() != dest_ty.ptrAddressSpace()) break :src_array_ptr;
if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :src_array_ptr;
if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :src_array_ptr;
const dst_elem_type = dest_ty.childType();
const dst_elem_type = dest_info.pointee_type;
switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
.ok => {},
.no_match => break :src_array_ptr,
}
switch (dest_ty.ptrSize()) {
switch (dest_info.size) {
.Slice => {
// *[N]T to []T
return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
@ -12036,7 +12051,7 @@ fn coerce(
// *[N]T to [*]T
// *[N:s]T to [*:s]T
// *[N:s]T to [*]T
if (dest_ty.sentinel()) |dst_sentinel| {
if (dest_info.sentinel) |dst_sentinel| {
if (array_ty.sentinel()) |src_sentinel| {
if (src_sentinel.eql(dst_sentinel, dst_elem_type)) {
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
@ -12051,9 +12066,24 @@ fn coerce(
}
// coercion to C pointer
if (dest_ty.ptrSize() == .C) {
if (inst_ty.zigTypeTag() == .Null) {
return sema.addConstant(dest_ty, Value.@"null");
if (dest_info.size == .C) {
switch (inst_ty.zigTypeTag()) {
.Null => {
return sema.addConstant(dest_ty, Value.@"null");
},
.ComptimeInt => {
const addr = try sema.coerce(block, Type.usize, inst, inst_src);
return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
},
.Int => {
const ptr_size_ty = switch (inst_ty.intInfo(target).signedness) {
.signed => Type.isize,
.unsigned => Type.usize,
};
const addr = try sema.coerce(block, ptr_size_ty, inst, inst_src);
return sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
},
else => {},
}
}
},
@ -13632,6 +13662,19 @@ fn resolvePeerTypes(
continue;
}
if (chosen_ty_tag == .Pointer and chosen_ty.ptrSize() == .C and
(candidate_ty_tag == .Int or candidate_ty_tag == .ComptimeInt))
{
continue;
}
if (candidate_ty_tag == .Pointer and candidate_ty.ptrSize() == .C and
(chosen_ty_tag == .Int or chosen_ty_tag == .ComptimeInt))
{
chosen = candidate;
chosen_i = candidate_i + 1;
continue;
}
if (chosen_ty_tag == .ComptimeFloat and candidate_ty_tag == .ComptimeInt)
continue;
if (chosen_ty_tag == .ComptimeInt and candidate_ty_tag == .ComptimeFloat) {

View File

@ -1106,7 +1106,7 @@ pub const DeclGen = struct {
return parent_ptr.constInBoundsGEP(&indices, indices.len);
}
},
.null_value => {
.null_value, .zero => {
const llvm_type = try self.llvmType(tv.ty);
return llvm_type.constNull();
},
@ -3180,8 +3180,9 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
const llvm_dest_ty = try self.dg.llvmType(inst_ty);
// TODO look into pulling this logic out into a different AIR instruction than bitcast
if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {
if (operand_ty.zigTypeTag() == .Int and inst_ty.zigTypeTag() == .Pointer) {
return self.builder.buildIntToPtr(operand, llvm_dest_ty, "");
} else if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {
const target = self.dg.module.getTarget();
const elem_ty = operand_ty.childType();
if (!isByRef(inst_ty)) {

View File

@ -4006,6 +4006,7 @@ pub const Type = extern union {
pub const @"u8" = initTag(.u8);
pub const @"bool" = initTag(.bool);
pub const @"usize" = initTag(.usize);
pub const @"isize" = initTag(.isize);
pub const @"comptime_int" = initTag(.comptime_int);
pub const @"void" = initTag(.void);
pub const @"type" = initTag(.type);

View File

@ -937,9 +937,10 @@ pub const Value = extern union {
}
}
/// Asserts the value is an integer and it fits in a u64
pub fn toUnsignedInt(self: Value) u64 {
switch (self.tag()) {
/// If the value fits in a u64, return it, otherwise null.
/// Asserts not undefined.
pub fn getUnsignedInt(val: Value) ?u64 {
switch (val.tag()) {
.zero,
.bool_false,
.the_only_possible_value, // i0, u0
@ -949,16 +950,21 @@ pub const Value = extern union {
.bool_true,
=> return 1,
.int_u64 => return self.castTag(.int_u64).?.data,
.int_i64 => return @intCast(u64, self.castTag(.int_i64).?.data),
.int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(u64) catch unreachable,
.int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(u64) catch unreachable,
.int_u64 => return val.castTag(.int_u64).?.data,
.int_i64 => return @intCast(u64, val.castTag(.int_i64).?.data),
.int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(u64) catch null,
.int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(u64) catch null,
.undef => unreachable,
else => unreachable,
else => return null,
}
}
/// Asserts the value is an integer and it fits in a u64
pub fn toUnsignedInt(val: Value) u64 {
return getUnsignedInt(val).?;
}
/// Asserts the value is an integer and it fits in a i64
pub fn toSignedInt(self: Value) i64 {
switch (self.tag()) {

View File

@ -58,3 +58,38 @@ test "initialize const optional C pointer to null" {
try expect(a == null);
comptime try expect(a == null);
}
test "assigning integer to C pointer" {
var x: i32 = 0;
var ptr: [*c]u8 = 0;
var ptr2: [*c]u8 = x;
if (false) {
ptr;
ptr2;
}
}
test "C pointer comparison and arithmetic" {
const S = struct {
fn doTheTest() !void {
var ptr1: [*c]u32 = 0;
var ptr2 = ptr1 + 10;
try expect(ptr1 == 0);
try expect(ptr1 >= 0);
try expect(ptr1 <= 0);
// expect(ptr1 < 1);
// expect(ptr1 < one);
// expect(1 > ptr1);
// expect(one > ptr1);
try expect(ptr1 < ptr2);
try expect(ptr2 > ptr1);
try expect(ptr2 >= 40);
try expect(ptr2 == 40);
try expect(ptr2 <= 40);
ptr2 -= 10;
try expect(ptr1 == ptr2);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}

View File

@ -19,41 +19,6 @@ fn testDerefPtrOneVal() !void {
try expect(@TypeOf(y.x) == void);
}
test "assigning integer to C pointer" {
var x: i32 = 0;
var ptr: [*c]u8 = 0;
var ptr2: [*c]u8 = x;
if (false) {
ptr;
ptr2;
}
}
test "C pointer comparison and arithmetic" {
const S = struct {
fn doTheTest() !void {
var ptr1: [*c]u32 = 0;
var ptr2 = ptr1 + 10;
try expect(ptr1 == 0);
try expect(ptr1 >= 0);
try expect(ptr1 <= 0);
// expect(ptr1 < 1);
// expect(ptr1 < one);
// expect(1 > ptr1);
// expect(one > ptr1);
try expect(ptr1 < ptr2);
try expect(ptr2 > ptr1);
try expect(ptr2 >= 40);
try expect(ptr2 == 40);
try expect(ptr2 <= 40);
ptr2 -= 10;
try expect(ptr1 == ptr2);
}
};
try S.doTheTest();
comptime try S.doTheTest();
}
test "peer type resolution with C pointers" {
var ptr_one: *u8 = undefined;
var ptr_many: [*]u8 = undefined;