Merge pull request #10857 from Luukdegram/wasm-tests

stage2: Wasm - implement field_ptr
This commit is contained in:
Andrew Kelley 2022-02-10 19:22:23 -05:00 committed by GitHub
commit f0400ad93e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 117 additions and 32 deletions

View File

@ -1106,6 +1106,20 @@ pub const DeclGen = struct {
}
return Result{ .appended = {} };
},
.Float => {
const float_bits = ty.floatBits(self.target());
if (float_bits > 64) {
return self.fail("Wasm TODO: Implement f80 and f128", .{});
}
switch (float_bits) {
16, 32 => try writer.writeIntLittle(u32, @bitCast(u32, val.toFloat(f32))),
64 => try writer.writeIntLittle(u64, @bitCast(u64, val.toFloat(f64))),
else => unreachable,
}
return Result{ .appended = {} };
},
.Enum => {
var int_buffer: Value.Payload.U64 = undefined;
const int_val = val.enumToInt(ty, &int_buffer);
@ -1334,10 +1348,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
defer self.gpa.free(param_types);
fn_ty.fnParamTypes(param_types);
var result: CallWValues = .{
.args = try self.gpa.alloc(WValue, param_types.len),
.args = &.{},
.return_value = .none,
};
errdefer self.gpa.free(result.args);
var args = std.ArrayList(WValue).init(self.gpa);
defer args.deinit();
const ret_ty = fn_ty.fnReturnType();
// Check if we store the result as a pointer to the stack rather than
// by value
@ -1350,18 +1366,18 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
switch (cc) {
.Naked => return result,
.Unspecified, .C => {
for (param_types) |ty, ty_index| {
for (param_types) |ty| {
if (!ty.hasRuntimeBits()) {
result.args[ty_index] = .{ .none = {} };
continue;
}
result.args[ty_index] = .{ .local = self.local_index };
try args.append(.{ .local = self.local_index });
self.local_index += 1;
}
},
else => return self.fail("TODO implement function parameters for cc '{}' on wasm", .{cc}),
}
result.args = args.toOwnedSlice();
return result;
}
@ -2060,6 +2076,26 @@ fn airWrapBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
if (val.isUndefDeep()) return self.emitUndefined(ty);
if (val.castTag(.decl_ref)) |decl_ref| {
const decl = decl_ref.data;
decl.markAlive();
const target_sym_index = decl.link.wasm.sym_index;
if (ty.isSlice()) {
var slice_len: Value.Payload.U64 = .{
.base = .{ .tag = .int_u64 },
.data = val.sliceLen(),
};
var slice_val: Value.Payload.Slice = .{
.base = .{ .tag = .slice },
.data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) },
};
return self.lowerConstant(Value.initPayload(&slice_val.base), ty);
} else if (decl.ty.zigTypeTag() == .Fn) {
try self.bin_file.addTableFunction(target_sym_index);
return WValue{ .function_index = target_sym_index };
} else return WValue{ .memory = target_sym_index };
}
switch (ty.zigTypeTag()) {
.Int => {
const int_info = ty.intInfo(self.target);
@ -2084,25 +2120,6 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
else => unreachable,
},
.Pointer => switch (val.tag()) {
.decl_ref => {
const decl = val.castTag(.decl_ref).?.data;
decl.markAlive();
const target_sym_index = decl.link.wasm.sym_index;
if (ty.isSlice()) {
var slice_len: Value.Payload.U64 = .{
.base = .{ .tag = .int_u64 },
.data = val.sliceLen(),
};
var slice_val: Value.Payload.Slice = .{
.base = .{ .tag = .slice },
.data = .{ .ptr = val.slicePtr(), .len = Value.initPayload(&slice_len.base) },
};
return self.lowerConstant(Value.initPayload(&slice_val.base), ty);
} else if (decl.ty.zigTypeTag() == .Fn) {
try self.bin_file.addTableFunction(target_sym_index);
return WValue{ .function_index = target_sym_index };
} else return WValue{ .memory = target_sym_index };
},
.elem_ptr => {
const elem_ptr = val.castTag(.elem_ptr).?.data;
const index = elem_ptr.index;
@ -2114,6 +2131,27 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
.offset = @intCast(u32, offset),
} };
},
.field_ptr => {
const field_ptr = val.castTag(.field_ptr).?.data;
const container = field_ptr.container_ptr;
const parent_ptr = try self.lowerConstant(container, ty);
const offset = switch (container.tag()) {
.decl_ref => blk: {
const decl_ref = container.castTag(.decl_ref).?.data;
if (decl_ref.ty.castTag(.@"struct")) |_| {
const offset = decl_ref.ty.structFieldOffset(field_ptr.field_index, self.target);
break :blk offset;
}
return self.fail("Wasm TODO: field_ptr decl_ref for type '{}'", .{decl_ref.ty});
},
else => |tag| return self.fail("Wasm TODO: Implement field_ptr for value tag: '{s}'", .{tag}),
};
return WValue{ .memory_offset = .{
.pointer = parent_ptr.memory,
.offset = @intCast(u32, offset),
} };
},
.int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
.zero, .null_value => return WValue{ .imm32 = 0 },
else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),
@ -2160,7 +2198,14 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
},
.Optional => if (ty.isPtrLikeOptional()) {
var buf: Type.Payload.ElemType = undefined;
return self.lowerConstant(val, ty.optionalChild(&buf));
const pl_ty = ty.optionalChild(&buf);
if (val.castTag(.opt_payload)) |payload| {
return self.lowerConstant(payload.data, pl_ty);
} else if (val.isNull()) {
return WValue{ .imm32 = 0 };
} else {
return self.lowerConstant(val, pl_ty);
}
} else {
const is_pl = val.tag() == .opt_payload;
return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };

View File

@ -428,7 +428,7 @@ pub fn addTableFunction(self: *Wasm, symbol_index: u32) !void {
fn mapFunctionTable(self: *Wasm) void {
var it = self.function_table.valueIterator();
var index: u32 = 0;
var index: u32 = 1;
while (it.next()) |value_ptr| : (index += 1) {
value_ptr.* = index;
}
@ -821,7 +821,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
try leb.writeULEB128(writer, wasm.reftype(.funcref));
try emitLimits(writer, .{
.min = @intCast(u32, self.function_table.count()),
.min = @intCast(u32, self.function_table.count()) + 1,
.max = null,
});
@ -931,7 +931,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void {
var flags: u32 = 0x2; // Yes we have a table
try leb.writeULEB128(writer, flags);
try leb.writeULEB128(writer, @as(u32, 0)); // index of that table. TODO: Store synthetic symbols
try emitInit(writer, .{ .i32_const = 0 });
try emitInit(writer, .{ .i32_const = 1 }); // We start at index 1, so unresolved function pointers are invalid
try leb.writeULEB128(writer, @as(u8, 0));
try leb.writeULEB128(writer, @intCast(u32, self.function_table.count()));
var symbol_it = self.function_table.keyIterator();

View File

@ -23,6 +23,8 @@ test {
_ = @import("behavior/bugs/1914.zig");
_ = @import("behavior/bugs/2006.zig");
_ = @import("behavior/bugs/2346.zig");
_ = @import("behavior/bugs/2578.zig");
_ = @import("behavior/bugs/3007.zig");
_ = @import("behavior/bugs/3112.zig");
_ = @import("behavior/bugs/3367.zig");
_ = @import("behavior/bugs/6850.zig");
@ -58,9 +60,11 @@ test {
_ = @import("behavior/bugs/4954.zig");
_ = @import("behavior/byval_arg_var.zig");
_ = @import("behavior/call.zig");
_ = @import("behavior/cast_llvm.zig");
_ = @import("behavior/defer.zig");
_ = @import("behavior/enum.zig");
_ = @import("behavior/error.zig");
_ = @import("behavior/fn.zig");
_ = @import("behavior/for.zig");
_ = @import("behavior/generics.zig");
_ = @import("behavior/if.zig");
@ -93,14 +97,10 @@ test {
if (builtin.zig_backend != .stage2_c) {
// Tests that pass for stage1 and the llvm backend.
_ = @import("behavior/atomics.zig");
_ = @import("behavior/bugs/2578.zig");
_ = @import("behavior/bugs/3007.zig");
_ = @import("behavior/bugs/9584.zig");
_ = @import("behavior/cast_llvm.zig");
_ = @import("behavior/error_llvm.zig");
_ = @import("behavior/eval.zig");
_ = @import("behavior/floatop.zig");
_ = @import("behavior/fn.zig");
_ = @import("behavior/math.zig");
_ = @import("behavior/maximum_minimum.zig");
_ = @import("behavior/merge_error_sets.zig");

View File

@ -1,3 +1,5 @@
const builtin = @import("builtin");
const Foo = struct {
y: u8,
};
@ -10,5 +12,9 @@ fn bar(pointer: ?*anyopaque) void {
}
test "fixed" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
bar(t);
}

View File

@ -1,4 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
const Foo = struct {
free: bool,
@ -18,6 +19,10 @@ fn get_foo() Foo.FooError!*Foo {
}
test "fixed" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
default_foo = get_foo() catch null; // This Line
try std.testing.expect(!default_foo.?.free);
}

View File

@ -6,6 +6,8 @@ const maxInt = std.math.maxInt;
const native_endian = builtin.target.cpu.arch.endian();
test "pointer reinterpret const float to int" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
// The hex representation is 0x3fe3333333333303.
const float: f64 = 5.99999999999994648725e-01;
const float_ptr = &float;
@ -18,6 +20,8 @@ test "pointer reinterpret const float to int" {
}
test "@floatToInt" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
try testFloatToInts();
comptime try testFloatToInts();
}
@ -33,6 +37,8 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
}
test "implicit cast from [*]T to ?*anyopaque" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
var a = [_]u8{ 3, 2, 1 };
var runtime_zero: usize = 0;
incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
@ -49,6 +55,7 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void {
test "compile time int to ptr of function" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
try foobar(FUNCTION_CONSTANT);
}
@ -61,6 +68,8 @@ fn foobar(func: PFN_void) !void {
}
test "implicit ptr to *anyopaque" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
var a: u32 = 1;
var ptr: *align(@alignOf(u32)) anyopaque = &a;
var b: *u32 = @ptrCast(*u32, ptr);
@ -87,6 +96,7 @@ fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A {
}
test "peer type resolution: [0]u8 and []const u8" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
comptime {
@ -103,6 +113,7 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
}
test "implicitly cast from [N]T to ?[]const T" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
}
@ -112,6 +123,7 @@ fn castToOptionalSlice() ?[]const u8 {
}
test "cast u128 to f128 and back" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
comptime try testCast128();
try testCast128();
}
@ -129,6 +141,7 @@ fn cast128Float(x: u128) f128 {
}
test "implicit cast from *[N]T to ?[*]T" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
var x: ?[*]u16 = null;
var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
@ -140,6 +153,7 @@ test "implicit cast from *[N]T to ?[*]T" {
}
test "implicit cast from *T to ?*anyopaque" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
var a: u8 = 1;
incrementVoidPtrValue(&a);
try std.testing.expect(a == 2);
@ -150,6 +164,7 @@ fn incrementVoidPtrValue(value: ?*anyopaque) void {
}
test "implicit cast *[0]T to E![]const u8" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
var x = @as(anyerror![]const u8, &[0]u8{});
try expect((x catch unreachable).len == 0);
}
@ -160,11 +175,13 @@ test "cast from array reference to fn: comptime fn ptr" {
try expect(@ptrToInt(f) == @ptrToInt(&global_array));
}
test "cast from array reference to fn: runtime fn ptr" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
var f = @ptrCast(*const fn () callconv(.C) void, &global_array);
try expect(@ptrToInt(f) == @ptrToInt(&global_array));
}
test "*const [N]null u8 to ?[]const u8" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var a = "Hello";
@ -196,6 +213,7 @@ test "cast between [*c]T and ?[*:0]T on fn parameter" {
var global_struct: struct { f0: usize } = undefined;
test "assignment to optional pointer result loc" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
var foo: struct { ptr: ?*anyopaque } = .{ .ptr = &global_struct };
try expect(foo.ptr.? == @ptrCast(*anyopaque, &global_struct));
}
@ -217,6 +235,8 @@ fn boolToStr(b: bool) []const u8 {
}
test "cast f16 to wider types" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var x: f16 = 1234.0;
@ -230,6 +250,9 @@ test "cast f16 to wider types" {
}
test "cast f128 to narrower types" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
var x: f128 = 1234.0;
@ -243,6 +266,7 @@ test "cast f128 to narrower types" {
}
test "peer type resolution: unreachable, null, slice" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest(num: usize, word: []const u8) !void {
const result = switch (num) {

View File

@ -75,6 +75,7 @@ test "return inner function which references comptime variable of outer function
}
test "discard the result of a function that returns a struct" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
const S = struct {
fn entry() void {
_ = func();
@ -94,6 +95,7 @@ test "discard the result of a function that returns a struct" {
}
test "inline function call that calls optional function pointer, return pointer at callsite interacts correctly with callsite return type" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
const S = struct {
@ -139,6 +141,7 @@ fn fnWithUnreachable() noreturn {
}
test "extern struct with stdcallcc fn pointer" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
const S = extern struct {
@ -252,6 +255,7 @@ test "implicit cast fn call result to optional in field result" {
}
test "void parameters" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
try voidFun(1, void{}, 2, {});
}
fn voidFun(a: i32, b: void, c: i32, d: void) !void {
@ -306,6 +310,7 @@ fn numberLiteralArg(a: anytype) !void {
}
test "function call with anon list literal" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
const S = struct {
fn doTheTest() !void {
try consumeVec(.{ 9, 8, 7 });