diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index b0c24be03b..bcad7cac19 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -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 }; diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index d62f3a4201..81d77d5b66 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -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(); diff --git a/test/behavior.zig b/test/behavior.zig index 3e4ae36bfc..a1d8e9bef9 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -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"); diff --git a/test/behavior/bugs/2578.zig b/test/behavior/bugs/2578.zig index b27d73415e..15f5bf0e53 100644 --- a/test/behavior/bugs/2578.zig +++ b/test/behavior/bugs/2578.zig @@ -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); } diff --git a/test/behavior/bugs/3007.zig b/test/behavior/bugs/3007.zig index c08be3676a..0b3cbdc56d 100644 --- a/test/behavior/bugs/3007.zig +++ b/test/behavior/bugs/3007.zig @@ -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); } diff --git a/test/behavior/cast_llvm.zig b/test/behavior/cast_llvm.zig index 79c2243a50..6f9b77b8f2 100644 --- a/test/behavior/cast_llvm.zig +++ b/test/behavior/cast_llvm.zig @@ -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) { diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index ebbbfda67b..17a3d9a93b 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -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 });