authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-03 20:23:46-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-03 20:23:46-05:00
log71e0cca7a7957e2f024d2985318e478aa6fb1451
tree8a85869adb92126d3fbaca52d4b0c0607ef3d7de
parent4ca9a8d192f4c800f10cdb3bd39c94922b6fb9b8
parent588b88b98753f02061e562a9c15c2396bcd95dee
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10780 from Luukdegram/wasm-behavior-tests

stage2: Wasm - Account for stack alignment

10 files changed, 338 insertions(+), 97 deletions(-)

src/arch/wasm/CodeGen.zig+263-86
...@@ -39,6 +39,14 @@ const WValue = union(enum) {...@@ -39,6 +39,14 @@ const WValue = union(enum) {
39 /// Note: The value contains the symbol index, rather than the actual address39 /// Note: The value contains the symbol index, rather than the actual address
40 /// as we use this to perform the relocation.40 /// as we use this to perform the relocation.
41 memory: u32,41 memory: u32,
42 /// A value that represents a parent pointer and an offset
43 /// from that pointer. i.e. when slicing with constant values.
44 memory_offset: struct {
45 /// The symbol of the parent pointer
46 pointer: u32,
47 /// Offset will be set as addend when relocating
48 offset: u32,
49 },
42 /// Represents a function pointer50 /// Represents a function pointer
43 /// In wasm function pointers are indexes into a function table,51 /// In wasm function pointers are indexes into a function table,
44 /// rather than an address in the data section.52 /// rather than an address in the data section.
...@@ -552,6 +560,9 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{},...@@ -552,6 +560,9 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{},
552/// When a function is executing, we store the the current stack pointer's value within this local.560/// When a function is executing, we store the the current stack pointer's value within this local.
553/// This value is then used to restore the stack pointer to the original value at the return of the function.561/// This value is then used to restore the stack pointer to the original value at the return of the function.
554initial_stack_value: WValue = .none,562initial_stack_value: WValue = .none,
563/// The current stack pointer substracted with the stack size. From this value, we will calculate
564/// all offsets of the stack values.
565bottom_stack_value: WValue = .none,
555/// Arguments of this function declaration566/// Arguments of this function declaration
556/// This will be set after `resolveCallingConventionValues`567/// This will be set after `resolveCallingConventionValues`
557args: []WValue = &.{},568args: []WValue = &.{},
...@@ -559,6 +570,14 @@ args: []WValue = &.{},...@@ -559,6 +570,14 @@ args: []WValue = &.{},
559/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated570/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
560/// before this function returns its execution to the caller.571/// before this function returns its execution to the caller.
561return_value: WValue = .none,572return_value: WValue = .none,
573/// The size of the stack this function occupies. In the function prologue
574/// we will move the stack pointer by this number, forward aligned with the `stack_alignment`.
575stack_size: u32 = 0,
576/// The stack alignment, which is 16 bytes by default. This is specified by the
577/// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
578/// and also what the llvm backend will emit.
579/// However, local variables or the usage of `@setAlignStack` can overwrite this default.
580stack_alignment: u32 = 16,
562581
563const InnerError = error{582const InnerError = error{
564 OutOfMemory,583 OutOfMemory,
...@@ -598,7 +617,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {...@@ -598,7 +617,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
598 // means we must generate it from a constant.617 // means we must generate it from a constant.
599 const val = self.air.value(ref).?;618 const val = self.air.value(ref).?;
600 const ty = self.air.typeOf(ref);619 const ty = self.air.typeOf(ref);
601 if (!ty.hasRuntimeBits() and !ty.isInt()) return WValue{ .none = {} };620 if (!ty.hasRuntimeBits() and !ty.isInt()) {
621 gop.value_ptr.* = WValue{ .none = {} };
622 return gop.value_ptr.*;
623 }
602624
603 // When we need to pass the value by reference (such as a struct), we will625 // When we need to pass the value by reference (such as a struct), we will
604 // leverage `genTypedValue` to lower the constant to bytes and emit it626 // leverage `genTypedValue` to lower the constant to bytes and emit it
...@@ -643,13 +665,6 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void {...@@ -643,13 +665,6 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void {
643 try self.mir_instructions.append(self.gpa, inst);665 try self.mir_instructions.append(self.gpa, inst);
644}666}
645667
646/// Inserts a Mir instruction at the given `offset`.
647/// Asserts offset is within bound.
648fn addInstAt(self: *Self, offset: usize, inst: Mir.Inst) error{OutOfMemory}!void {
649 try self.mir_instructions.ensureUnusedCapacity(self.gpa, 1);
650 self.mir_instructions.insertAssumeCapacity(offset, inst);
651}
652
653fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void {668fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
654 try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } });669 try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
655}670}
...@@ -754,7 +769,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {...@@ -754,7 +769,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {
754 .imm64 => |val| try self.addImm64(val),769 .imm64 => |val| try self.addImm64(val),
755 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),770 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
756 .float64 => |val| try self.addFloat64(val),771 .float64 => |val| try self.addFloat64(val),
757 .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation772 .memory => |ptr| {
773 const extra_index = try self.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 });
774 try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
775 },
776 .memory_offset => |mem_off| {
777 const extra_index = try self.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset });
778 try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } });
779 },
758 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation780 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
759 }781 }
760}782}
...@@ -827,10 +849,43 @@ pub fn genFunc(self: *Self) InnerError!void {...@@ -827,10 +849,43 @@ pub fn genFunc(self: *Self) InnerError!void {
827 try self.addTag(.@"unreachable");849 try self.addTag(.@"unreachable");
828 }850 }
829 }851 }
830
831 // End of function body852 // End of function body
832 try self.addTag(.end);853 try self.addTag(.end);
833854
855 // check if we have to initialize and allocate anything into the stack frame.
856 // If so, create enough stack space and insert the instructions at the front of the list.
857 if (self.stack_size > 0) {
858 var prologue = std.ArrayList(Mir.Inst).init(self.gpa);
859 defer prologue.deinit();
860
861 // load stack pointer
862 try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });
863 // store stack pointer so we can restore it when we return from the function
864 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.initial_stack_value.local } });
865 // get the total stack size
866 const aligned_stack = std.mem.alignForwardGeneric(u32, self.stack_size, self.stack_alignment);
867 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, aligned_stack) } });
868 // substract it from the current stack pointer
869 try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } });
870 // Get negative stack aligment
871 try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, self.stack_alignment) * -1 } });
872 // Bit and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment
873 try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } });
874 // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets
875 try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.bottom_stack_value.local } });
876 // Store the current stack pointer value into the global stack pointer so other function calls will
877 // start from this value instead and not overwrite the current stack.
878 try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } });
879
880 // reserve space and insert all prologue instructions at the front of the instruction list
881 // We insert them in reserve order as there is no insertSlice in multiArrayList.
882 try self.mir_instructions.ensureUnusedCapacity(self.gpa, prologue.items.len);
883 for (prologue.items) |_, index| {
884 const inst = prologue.items[prologue.items.len - 1 - index];
885 self.mir_instructions.insertAssumeCapacity(0, inst);
886 }
887 }
888
834 var mir: Mir = .{889 var mir: Mir = .{
835 .instructions = self.mir_instructions.toOwnedSlice(),890 .instructions = self.mir_instructions.toOwnedSlice(),
836 .extra = self.mir_extra.toOwnedSlice(self.gpa),891 .extra = self.mir_extra.toOwnedSlice(self.gpa),
...@@ -927,7 +982,7 @@ pub const DeclGen = struct {...@@ -927,7 +982,7 @@ pub const DeclGen = struct {
927 .function => val.castTag(.function).?.data.owner_decl,982 .function => val.castTag(.function).?.data.owner_decl,
928 else => unreachable,983 else => unreachable,
929 };984 };
930 return try self.lowerDeclRef(ty, val, fn_decl);985 return try self.lowerDeclRefValue(ty, val, fn_decl, 0);
931 },986 },
932 .Optional => {987 .Optional => {
933 var opt_buf: Type.Payload.ElemType = undefined;988 var opt_buf: Type.Payload.ElemType = undefined;
...@@ -1115,11 +1170,11 @@ pub const DeclGen = struct {...@@ -1115,11 +1170,11 @@ pub const DeclGen = struct {
1115 .Pointer => switch (val.tag()) {1170 .Pointer => switch (val.tag()) {
1116 .variable => {1171 .variable => {
1117 const decl = val.castTag(.variable).?.data.owner_decl;1172 const decl = val.castTag(.variable).?.data.owner_decl;
1118 return self.lowerDeclRef(ty, val, decl);1173 return self.lowerDeclRefValue(ty, val, decl, 0);
1119 },1174 },
1120 .decl_ref => {1175 .decl_ref => {
1121 const decl = val.castTag(.decl_ref).?.data;1176 const decl = val.castTag(.decl_ref).?.data;
1122 return self.lowerDeclRef(ty, val, decl);1177 return self.lowerDeclRefValue(ty, val, decl, 0);
1123 },1178 },
1124 .slice => {1179 .slice => {
1125 const slice = val.castTag(.slice).?.data;1180 const slice = val.castTag(.slice).?.data;
...@@ -1139,6 +1194,13 @@ pub const DeclGen = struct {...@@ -1139,6 +1194,13 @@ pub const DeclGen = struct {
1139 try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8));1194 try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8));
1140 return Result{ .appended = {} };1195 return Result{ .appended = {} };
1141 },1196 },
1197 .elem_ptr => {
1198 const elem_ptr = val.castTag(.elem_ptr).?.data;
1199 const elem_size = ty.childType().abiSize(self.target());
1200 const offset = elem_ptr.index * elem_size;
1201 return self.lowerParentPtr(elem_ptr.array_ptr, @intCast(usize, offset));
1202 },
1203 .int_u64 => return self.genTypedValue(Type.usize, val),
1142 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),1204 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
1143 },1205 },
1144 .ErrorUnion => {1206 .ErrorUnion => {
...@@ -1179,7 +1241,36 @@ pub const DeclGen = struct {...@@ -1179,7 +1241,36 @@ pub const DeclGen = struct {
1179 }1241 }
1180 }1242 }
11811243
1182 fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result {1244 fn lowerParentPtr(self: *DeclGen, ptr_value: Value, offset: usize) InnerError!Result {
1245 switch (ptr_value.tag()) {
1246 .decl_ref => {
1247 const decl = ptr_value.castTag(.decl_ref).?.data;
1248 return self.lowerParentPtrDecl(ptr_value, decl, offset);
1249 },
1250 else => |tag| return self.fail("TODO: Implement lowerParentPtr for pointer value tag: {s}", .{tag}),
1251 }
1252 }
1253
1254 fn lowerParentPtrDecl(self: *DeclGen, ptr_val: Value, decl: *Module.Decl, offset: usize) InnerError!Result {
1255 decl.markAlive();
1256 var ptr_ty_payload: Type.Payload.ElemType = .{
1257 .base = .{ .tag = .single_mut_pointer },
1258 .data = decl.ty,
1259 };
1260 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
1261 return self.lowerDeclRefValue(ptr_ty, ptr_val, decl, offset);
1262 }
1263
1264 fn lowerDeclRefValue(
1265 self: *DeclGen,
1266 ty: Type,
1267 val: Value,
1268 /// The target decl that is being pointed to
1269 decl: *Module.Decl,
1270 /// When lowering to an indexed pointer, we can specify the offset
1271 /// which will then be used as 'addend' to the relocation.
1272 offset: usize,
1273 ) InnerError!Result {
1183 const writer = self.code.writer();1274 const writer = self.code.writer();
1184 if (ty.isSlice()) {1275 if (ty.isSlice()) {
1185 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1276 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
...@@ -1202,6 +1293,7 @@ pub const DeclGen = struct {...@@ -1202,6 +1293,7 @@ pub const DeclGen = struct {
1202 self.symbol_index, // source symbol index1293 self.symbol_index, // source symbol index
1203 decl.link.wasm.sym_index, // target symbol index1294 decl.link.wasm.sym_index, // target symbol index
1204 @intCast(u32, self.code.items.len), // offset1295 @intCast(u32, self.code.items.len), // offset
1296 @intCast(u32, offset), // addend
1205 ));1297 ));
1206 return Result{ .appended = {} };1298 return Result{ .appended = {} };
1207 }1299 }
...@@ -1254,22 +1346,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu...@@ -1254,22 +1346,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
1254 return result;1346 return result;
1255}1347}
12561348
1257/// Retrieves the stack pointer's value from the global variable and stores1349/// Creates a local for the initial stack value
1258/// it in a local
1259/// Asserts `initial_stack_value` is `.none`1350/// Asserts `initial_stack_value` is `.none`
1260fn initializeStack(self: *Self) !void {1351fn initializeStack(self: *Self) !void {
1261 assert(self.initial_stack_value == .none);1352 assert(self.initial_stack_value == .none);
1262 // reserve space for immediate value
1263 // get stack pointer global
1264 try self.addLabel(.global_get, 0);
1265
1266 // Reserve a local to store the current stack pointer1353 // Reserve a local to store the current stack pointer
1267 // We can later use this local to set the stack pointer back to the value1354 // We can later use this local to set the stack pointer back to the value
1268 // we have stored here.1355 // we have stored here.
1269 self.initial_stack_value = try self.allocLocal(Type.initTag(.i32));1356 self.initial_stack_value = try self.allocLocal(Type.usize);
12701357 // Also reserve a local to store the bottom stack value
1271 // save the value to the local1358 self.bottom_stack_value = try self.allocLocal(Type.usize);
1272 try self.addLabel(.local_set, self.initial_stack_value.local);
1273}1359}
12741360
1275/// Reads the stack pointer from `Context.initial_stack_value` and writes it1361/// Reads the stack pointer from `Context.initial_stack_value` and writes it
...@@ -1284,36 +1370,75 @@ fn restoreStackPointer(self: *Self) !void {...@@ -1284,36 +1370,75 @@ fn restoreStackPointer(self: *Self) !void {
1284 try self.addLabel(.global_set, 0);1370 try self.addLabel(.global_set, 0);
1285}1371}
12861372
1287/// Moves the stack pointer by given `offset`1373/// Saves the current stack size's stack pointer position into a given local
1288/// It does this by retrieving the stack pointer, subtracting `offset` and storing1374/// It does this by retrieving the bottom stack pointer, adding `self.stack_size` and storing
1289/// the result back into the stack pointer.1375/// the result back into the local.
1290fn moveStack(self: *Self, offset: u32, local: u32) !void {1376fn saveStack(self: *Self) !WValue {
1291 if (offset == 0) return;1377 const local = try self.allocLocal(Type.usize);
1292 try self.addLabel(.global_get, 0);1378 try self.addLabel(.local_get, self.bottom_stack_value.local);
1293 try self.addImm32(@bitCast(i32, offset));1379 try self.addImm32(@intCast(i32, self.stack_size));
1294 try self.addTag(.i32_sub);1380 try self.addTag(.i32_add);
1295 try self.addLabel(.local_tee, local);1381 try self.addLabel(.local_set, local.local);
1296 try self.addLabel(.global_set, 0);1382 return local;
1297}1383}
12981384
1299/// From a given type, will create space on the virtual stack to store the value of such type.1385/// From a given type, will create space on the virtual stack to store the value of such type.
1300/// This returns a `WValue` with its active tag set to `local`, containing the index to the local1386/// This returns a `WValue` with its active tag set to `local`, containing the index to the local
1301/// that points to the position on the virtual stack. This function should be used instead of1387/// that points to the position on the virtual stack. This function should be used instead of
1302/// moveStack unless a local was already created to store the point.1388/// moveStack unless a local was already created to store the pointer.
1303///1389///
1304/// Asserts Type has codegenbits1390/// Asserts Type has codegenbits
1305fn allocStack(self: *Self, ty: Type) !WValue {1391fn allocStack(self: *Self, ty: Type) !WValue {
1306 assert(ty.hasRuntimeBits());1392 assert(ty.hasRuntimeBits());
1393 if (self.initial_stack_value == .none) {
1394 try self.initializeStack();
1395 }
13071396
1308 // calculate needed stack space
1309 const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {1397 const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {
1310 return self.fail("Given type '{}' too big to fit into stack frame", .{ty});1398 return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ ty, ty.abiSize(self.target) });
1311 };1399 };
1400 const abi_align = ty.abiAlignment(self.target);
13121401
1313 // allocate a local using wasm's pointer size1402 if (abi_align > self.stack_alignment) {
1314 const local = try self.allocLocal(Type.@"usize");1403 self.stack_alignment = abi_align;
1315 try self.moveStack(abi_size, local.local);1404 }
1316 return local;1405
1406 const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align);
1407 defer self.stack_size = offset + abi_size;
1408
1409 // store the stack pointer and return a local to it
1410 return self.saveStack();
1411}
1412
1413/// From a given AIR instruction generates a pointer to the stack where
1414/// the value of its type will live.
1415/// This is different from allocStack where this will use the pointer's alignment
1416/// if it is set, to ensure the stack alignment will be set correctly.
1417fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {
1418 const ptr_ty = self.air.typeOfIndex(inst);
1419 const pointee_ty = ptr_ty.childType();
1420
1421 if (self.initial_stack_value == .none) {
1422 try self.initializeStack();
1423 }
1424
1425 if (!pointee_ty.hasRuntimeBits()) {
1426 return self.allocStack(Type.usize); // create a value containing just the stack pointer.
1427 }
1428
1429 const abi_alignment = ptr_ty.ptrAlignment(self.target);
1430 const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) catch {
1431 return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ pointee_ty, pointee_ty.abiSize(self.target) });
1432 };
1433 if (abi_alignment > self.stack_alignment) {
1434 self.stack_alignment = abi_alignment;
1435 }
1436
1437 const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment);
1438 defer self.stack_size = offset + abi_size;
1439
1440 // store the stack pointer and return a local to it
1441 return self.saveStack();
1317}1442}
13181443
1319/// From given zig bitsize, returns the wasm bitsize1444/// From given zig bitsize, returns the wasm bitsize
...@@ -1592,6 +1717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -1592,6 +1717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
1592fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1717fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1593 const un_op = self.air.instructions.items(.data)[inst].un_op;1718 const un_op = self.air.instructions.items(.data)[inst].un_op;
1594 const operand = try self.resolveInst(un_op);1719 const operand = try self.resolveInst(un_op);
1720
1595 // result must be stored in the stack and we return a pointer1721 // result must be stored in the stack and we return a pointer
1596 // to the stack instead1722 // to the stack instead
1597 if (self.return_value != .none) {1723 if (self.return_value != .none) {
...@@ -1601,7 +1727,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1601,7 +1727,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1601 }1727 }
1602 try self.restoreStackPointer();1728 try self.restoreStackPointer();
1603 try self.addTag(.@"return");1729 try self.addTag(.@"return");
1604 return .none;1730 return WValue{ .none = {} };
1605}1731}
16061732
1607fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1733fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1611,12 +1737,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1611,12 +1737,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1611 if (isByRef(child_type, self.target)) {1737 if (isByRef(child_type, self.target)) {
1612 return self.return_value;1738 return self.return_value;
1613 }1739 }
16141740 return self.allocStackPtr(inst);
1615 // Initialize the stack
1616 if (self.initial_stack_value == .none) {
1617 try self.initializeStack();
1618 }
1619 return self.allocStack(child_type);
1620}1741}
16211742
1622fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1743fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1708,20 +1829,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -1708,20 +1829,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1708}1829}
17091830
1710fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1831fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1711 const pointee_type = self.air.typeOfIndex(inst).childType();1832 return self.allocStackPtr(inst);
1712
1713 // Initialize the stack
1714 if (self.initial_stack_value == .none) {
1715 try self.initializeStack();
1716 }
1717
1718 if (!pointee_type.hasRuntimeBits()) {
1719 // when the pointee is zero-sized, we still want to create a pointer.
1720 // but instead use a default pointer type as storage.
1721 const zero_ptr = try self.allocStack(Type.usize);
1722 return zero_ptr;
1723 }
1724 return self.allocStack(pointee_type);
1725}1833}
17261834
1727fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {1835fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -1741,11 +1849,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1741,11 +1849,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1741 const err_ty = ty.errorUnionSet();1849 const err_ty = ty.errorUnionSet();
1742 const pl_ty = ty.errorUnionPayload();1850 const pl_ty = ty.errorUnionPayload();
1743 if (!pl_ty.hasRuntimeBits()) {1851 if (!pl_ty.hasRuntimeBits()) {
1744 const err_val = try self.load(rhs, err_ty, 0);1852 return self.store(lhs, rhs, err_ty, 0);
1745 return self.store(lhs, err_val, err_ty, 0);
1746 }1853 }
17471854
1748 return try self.memCopy(ty, lhs, rhs);1855 return self.memCopy(ty, lhs, rhs);
1749 },1856 },
1750 .Optional => {1857 .Optional => {
1751 if (ty.isPtrLikeOptional()) {1858 if (ty.isPtrLikeOptional()) {
...@@ -1760,7 +1867,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1760,7 +1867,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1760 return self.memCopy(ty, lhs, rhs);1867 return self.memCopy(ty, lhs, rhs);
1761 },1868 },
1762 .Struct, .Array, .Union => {1869 .Struct, .Array, .Union => {
1763 return try self.memCopy(ty, lhs, rhs);1870 return self.memCopy(ty, lhs, rhs);
1764 },1871 },
1765 .Pointer => {1872 .Pointer => {
1766 if (ty.isSlice()) {1873 if (ty.isSlice()) {
...@@ -1775,7 +1882,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro...@@ -1775,7 +1882,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
1775 }1882 }
1776 },1883 },
1777 .Int => if (ty.intInfo(self.target).bits > 64) {1884 .Int => if (ty.intInfo(self.target).bits > 64) {
1778 return try self.memCopy(ty, lhs, rhs);1885 return self.memCopy(ty, lhs, rhs);
1779 },1886 },
1780 else => {},1887 else => {},
1781 }1888 }
...@@ -1974,6 +2081,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {...@@ -1974,6 +2081,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
1974 return WValue{ .function_index = target_sym_index };2081 return WValue{ .function_index = target_sym_index };
1975 } else return WValue{ .memory = target_sym_index };2082 } else return WValue{ .memory = target_sym_index };
1976 },2083 },
2084 .elem_ptr => {
2085 const elem_ptr = val.castTag(.elem_ptr).?.data;
2086 const index = elem_ptr.index;
2087 const offset = index * ty.childType().abiSize(self.target);
2088 const array_ptr = try self.lowerConstant(elem_ptr.array_ptr, ty);
2089
2090 return WValue{ .memory_offset = .{
2091 .pointer = array_ptr.memory,
2092 .offset = @intCast(u32, offset),
2093 } };
2094 },
1977 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },2095 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
1978 .zero, .null_value => return WValue{ .imm32 = 0 },2096 .zero, .null_value => return WValue{ .imm32 = 0 },
1979 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),2097 else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),
...@@ -2524,11 +2642,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue...@@ -2524,11 +2642,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue
2524 if (isByRef(payload_ty, self.target)) {2642 if (isByRef(payload_ty, self.target)) {
2525 return self.buildPointerOffset(operand, offset, .new);2643 return self.buildPointerOffset(operand, offset, .new);
2526 }2644 }
2527 return try self.load(operand, payload_ty, offset);2645 return self.load(operand, payload_ty, offset);
2528}2646}
25292647
2530fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2648fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2531 if (self.liveness.isUnused(inst)) return WValue.none;2649 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
25322650
2533 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2651 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2534 const operand = try self.resolveInst(ty_op.operand);2652 const operand = try self.resolveInst(ty_op.operand);
...@@ -2538,11 +2656,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2538,11 +2656,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2538 return operand;2656 return operand;
2539 }2657 }
25402658
2541 return try self.load(operand, err_ty.errorUnionSet(), 0);2659 return self.load(operand, err_ty.errorUnionSet(), 0);
2542}2660}
25432661
2544fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2662fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2545 if (self.liveness.isUnused(inst)) return WValue.none;2663 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2664
2546 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2665 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2547 const operand = try self.resolveInst(ty_op.operand);2666 const operand = try self.resolveInst(ty_op.operand);
25482667
...@@ -2564,11 +2683,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2564,11 +2683,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2564}2683}
25652684
2566fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2685fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2567 if (self.liveness.isUnused(inst)) return WValue.none;2686 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2687
2568 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2688 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2569 const operand = try self.resolveInst(ty_op.operand);2689 const operand = try self.resolveInst(ty_op.operand);
2570 const err_ty = self.air.getRefType(ty_op.ty);2690 const err_ty = self.air.getRefType(ty_op.ty);
25712691
2692 if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand;
2693
2572 const err_union = try self.allocStack(err_ty);2694 const err_union = try self.allocStack(err_ty);
2573 // TODO: Also write 'undefined' to the payload2695 // TODO: Also write 'undefined' to the payload
2574 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);2696 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);
...@@ -2750,16 +2872,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2750,16 +2872,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2750}2872}
27512873
2752fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2874fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2753 if (self.liveness.isUnused(inst)) return WValue.none;2875 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
27542876
2755 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2877 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2756 const operand = try self.resolveInst(ty_op.operand);2878 const operand = try self.resolveInst(ty_op.operand);
27572879
2758 return try self.load(operand, Type.usize, self.ptrSize());2880 return self.load(operand, Type.usize, self.ptrSize());
2759}2881}
27602882
2761fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2883fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2762 if (self.liveness.isUnused(inst)) return WValue.none;2884 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
27632885
2764 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2886 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2765 const slice_ty = self.air.typeOf(bin_op.lhs);2887 const slice_ty = self.air.typeOf(bin_op.lhs);
...@@ -2784,7 +2906,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2784,7 +2906,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2784 if (isByRef(elem_ty, self.target)) {2906 if (isByRef(elem_ty, self.target)) {
2785 return result;2907 return result;
2786 }2908 }
2787 return try self.load(result, elem_ty, 0);2909 return self.load(result, elem_ty, 0);
2788}2910}
27892911
2790fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2912fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2812,10 +2934,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2812,10 +2934,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2812}2934}
28132935
2814fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2936fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2815 if (self.liveness.isUnused(inst)) return WValue.none;2937 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2816 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2938 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2817 const operand = try self.resolveInst(ty_op.operand);2939 const operand = try self.resolveInst(ty_op.operand);
2818 return try self.load(operand, Type.usize, 0);2940 return self.load(operand, Type.usize, 0);
2819}2941}
28202942
2821fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {2943fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2880,7 +3002,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2880,7 +3002,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28803002
2881fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3003fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2882 const un_op = self.air.instructions.items(.data)[inst].un_op;3004 const un_op = self.air.instructions.items(.data)[inst].un_op;
2883 return try self.resolveInst(un_op);3005 return self.resolveInst(un_op);
2884}3006}
28853007
2886fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3008fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2912,7 +3034,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2912,7 +3034,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2912fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3034fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2913 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };3035 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
2914 const un_op = self.air.instructions.items(.data)[inst].un_op;3036 const un_op = self.air.instructions.items(.data)[inst].un_op;
2915 return try self.resolveInst(un_op);3037 return self.resolveInst(un_op);
2916}3038}
29173039
2918fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3040fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2927,7 +3049,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2927,7 +3049,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29273049
2928 // load pointer onto the stack3050 // load pointer onto the stack
2929 if (ptr_ty.isSlice()) {3051 if (ptr_ty.isSlice()) {
2930 const ptr_local = try self.load(pointer, ptr_ty, 0);3052 const ptr_local = try self.load(pointer, Type.usize, 0);
2931 try self.addLabel(.local_get, ptr_local.local);3053 try self.addLabel(.local_get, ptr_local.local);
2932 } else {3054 } else {
2933 try self.emitWValue(pointer);3055 try self.emitWValue(pointer);
...@@ -2944,7 +3066,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2944,7 +3066,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
2944 if (isByRef(elem_ty, self.target)) {3066 if (isByRef(elem_ty, self.target)) {
2945 return result;3067 return result;
2946 }3068 }
2947 return try self.load(result, elem_ty, 0);3069 return self.load(result, elem_ty, 0);
2948}3070}
29493071
2950fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3072fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -2960,7 +3082,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -2960,7 +3082,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29603082
2961 // load pointer onto the stack3083 // load pointer onto the stack
2962 if (ptr_ty.isSlice()) {3084 if (ptr_ty.isSlice()) {
2963 const ptr_local = try self.load(ptr, ptr_ty, 0);3085 const ptr_local = try self.load(ptr, Type.usize, 0);
2964 try self.addLabel(.local_get, ptr_local.local);3086 try self.addLabel(.local_get, ptr_local.local);
2965 } else {3087 } else {
2966 try self.emitWValue(ptr);3088 try self.emitWValue(ptr);
...@@ -3094,7 +3216,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3094,7 +3216,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3094 if (isByRef(elem_ty, self.target)) {3216 if (isByRef(elem_ty, self.target)) {
3095 return result;3217 return result;
3096 }3218 }
3097 return try self.load(result, elem_ty, 0);3219 return self.load(result, elem_ty, 0);
3098}3220}
30993221
3100fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3222fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
...@@ -3138,8 +3260,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {...@@ -3138,8 +3260,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
3138 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3260 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3139 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);3261 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
31403262
3141 _ = elements;3263 switch (vector_ty.zigTypeTag()) {
3142 return self.fail("TODO: Wasm backend: implement airVectorInit", .{});3264 .Vector => return self.fail("TODO: Wasm backend: implement airVectorInit for vectors", .{}),
3265 .Array => {
3266 const result = try self.allocStack(vector_ty);
3267 const elem_ty = vector_ty.childType();
3268 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3269
3270 // When the element type is by reference, we must copy the entire
3271 // value. It is therefore safer to move the offset pointer and store
3272 // each value individually, instead of using store offsets.
3273 if (isByRef(elem_ty, self.target)) {
3274 // copy stack pointer into a temporary local, which is
3275 // moved for each element to store each value in the right position.
3276 const offset = try self.allocLocal(Type.usize);
3277 try self.emitWValue(result);
3278 try self.addLabel(.local_set, offset.local);
3279 for (elements) |elem, elem_index| {
3280 const elem_val = try self.resolveInst(elem);
3281 try self.store(offset, elem_val, elem_ty, 0);
3282
3283 if (elem_index < elements.len - 1) {
3284 _ = try self.buildPointerOffset(offset, elem_size, .modify);
3285 }
3286 }
3287 } else {
3288 var offset: u32 = 0;
3289 for (elements) |elem| {
3290 const elem_val = try self.resolveInst(elem);
3291 try self.store(result, elem_val, elem_ty, offset);
3292 offset += elem_size;
3293 }
3294 }
3295 return result;
3296 },
3297 .Struct => {
3298 const tuple = vector_ty.castTag(.tuple).?.data;
3299 const result = try self.allocStack(vector_ty);
3300 const offset = try self.allocLocal(Type.usize); // pointer to offset
3301 try self.emitWValue(result);
3302 try self.addLabel(.local_set, offset.local);
3303 for (elements) |elem, elem_index| {
3304 if (tuple.values[elem_index].tag() != .unreachable_value) continue;
3305
3306 const elem_ty = tuple.types[elem_index];
3307 const elem_size = @intCast(u32, elem_ty.abiSize(self.target));
3308 const value = try self.resolveInst(elem);
3309 try self.store(offset, value, elem_ty, 0);
3310
3311 if (elem_index < elements.len - 1) {
3312 _ = try self.buildPointerOffset(offset, elem_size, .modify);
3313 }
3314 }
3315
3316 return result;
3317 },
3318 else => unreachable,
3319 }
3143}3320}
31443321
3145fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {3322fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
src/arch/wasm/Emit.zig+6-4
...@@ -326,25 +326,27 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -326,25 +326,27 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
326}326}
327327
328fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {328fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
329 const symbol_index = emit.mir.instructions.items(.data)[inst].label;329 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
330 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;
330 const mem_offset = emit.offset() + 1;331 const mem_offset = emit.offset() + 1;
331 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;332 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;
332 if (is_wasm32) {333 if (is_wasm32) {
333 try emit.code.append(std.wasm.opcode(.i32_const));334 try emit.code.append(std.wasm.opcode(.i32_const));
334 var buf: [5]u8 = undefined;335 var buf: [5]u8 = undefined;
335 leb128.writeUnsignedFixed(5, &buf, symbol_index);336 leb128.writeUnsignedFixed(5, &buf, mem.pointer);
336 try emit.code.appendSlice(&buf);337 try emit.code.appendSlice(&buf);
337 } else {338 } else {
338 try emit.code.append(std.wasm.opcode(.i64_const));339 try emit.code.append(std.wasm.opcode(.i64_const));
339 var buf: [10]u8 = undefined;340 var buf: [10]u8 = undefined;
340 leb128.writeUnsignedFixed(10, &buf, symbol_index);341 leb128.writeUnsignedFixed(10, &buf, mem.pointer);
341 try emit.code.appendSlice(&buf);342 try emit.code.appendSlice(&buf);
342 }343 }
343344
344 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{345 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
345 .offset = mem_offset,346 .offset = mem_offset,
346 .index = symbol_index,347 .index = mem.pointer,
347 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,348 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
349 .addend = mem.offset,
348 });350 });
349}351}
350352
src/arch/wasm/Mir.zig+7
...@@ -546,3 +546,10 @@ pub const MemArg = struct {...@@ -546,3 +546,10 @@ pub const MemArg = struct {
546 offset: u32,546 offset: u32,
547 alignment: u32,547 alignment: u32,
548};548};
549
550/// Represents a memory address, which holds both the pointer
551/// or the parent pointer and the offset to it.
552pub const Memory = struct {
553 pointer: u32,
554 offset: u32,
555};
src/link/Wasm.zig+11-1
...@@ -345,10 +345,19 @@ pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32,...@@ -345,10 +345,19 @@ pub fn updateLocalSymbolCode(self: *Wasm, decl: *Module.Decl, symbol_index: u32,
345345
346/// For a given decl, find the given symbol index's atom, and create a relocation for the type.346/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
347/// Returns the given pointer address347/// Returns the given pointer address
348pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32, target_symbol_index: u32, offset: u32) !u32 {348pub fn getDeclVAddr(
349 self: *Wasm,
350 decl: *Module.Decl,
351 ty: Type,
352 symbol_index: u32,
353 target_symbol_index: u32,
354 offset: u32,
355 addend: u32,
356) !u32 {
349 const atom = decl.link.wasm.symbolAtom(symbol_index);357 const atom = decl.link.wasm.symbolAtom(symbol_index);
350 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;358 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
351 if (ty.zigTypeTag() == .Fn) {359 if (ty.zigTypeTag() == .Fn) {
360 std.debug.assert(addend == 0); // addend not allowed for function relocations
352 // We found a function pointer, so add it to our table,361 // We found a function pointer, so add it to our table,
353 // as function pointers are not allowed to be stored inside the data section.362 // as function pointers are not allowed to be stored inside the data section.
354 // They are instead stored in a function table which are called by index.363 // They are instead stored in a function table which are called by index.
...@@ -363,6 +372,7 @@ pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32...@@ -363,6 +372,7 @@ pub fn getDeclVAddr(self: *Wasm, decl: *Module.Decl, ty: Type, symbol_index: u32
363 .index = target_symbol_index,372 .index = target_symbol_index,
364 .offset = offset,373 .offset = offset,
365 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,374 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
375 .addend = addend,
366 });376 });
367 }377 }
368 // we do not know the final address at this point,378 // we do not know the final address at this point,
test/behavior.zig+6-6
...@@ -10,6 +10,7 @@ test {...@@ -10,6 +10,7 @@ test {
10 _ = @import("behavior/bugs/655.zig");10 _ = @import("behavior/bugs/655.zig");
11 _ = @import("behavior/bugs/656.zig");11 _ = @import("behavior/bugs/656.zig");
12 _ = @import("behavior/bugs/679.zig");12 _ = @import("behavior/bugs/679.zig");
13 _ = @import("behavior/bugs/1025.zig");
13 _ = @import("behavior/bugs/1111.zig");14 _ = @import("behavior/bugs/1111.zig");
14 _ = @import("behavior/bugs/1277.zig");15 _ = @import("behavior/bugs/1277.zig");
15 _ = @import("behavior/bugs/1310.zig");16 _ = @import("behavior/bugs/1310.zig");
...@@ -17,6 +18,8 @@ test {...@@ -17,6 +18,8 @@ test {
17 _ = @import("behavior/bugs/1486.zig");18 _ = @import("behavior/bugs/1486.zig");
18 _ = @import("behavior/bugs/1500.zig");19 _ = @import("behavior/bugs/1500.zig");
19 _ = @import("behavior/bugs/1735.zig");20 _ = @import("behavior/bugs/1735.zig");
21 _ = @import("behavior/bugs/1741.zig");
22 _ = @import("behavior/bugs/1914.zig");
20 _ = @import("behavior/bugs/2006.zig");23 _ = @import("behavior/bugs/2006.zig");
21 _ = @import("behavior/bugs/2346.zig");24 _ = @import("behavior/bugs/2346.zig");
22 _ = @import("behavior/bugs/3112.zig");25 _ = @import("behavior/bugs/3112.zig");
...@@ -38,7 +41,8 @@ test {...@@ -38,7 +41,8 @@ test {
38 _ = @import("behavior/struct.zig");41 _ = @import("behavior/struct.zig");
3942
40 if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) {43 if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) {
41 // Tests that pass for stage1, llvm backend, C backend, wasm backend.44 // Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend.
45 _ = @import("behavior/array_llvm.zig");
42 _ = @import("behavior/basic.zig");46 _ = @import("behavior/basic.zig");
43 _ = @import("behavior/bitcast.zig");47 _ = @import("behavior/bitcast.zig");
44 _ = @import("behavior/bugs/624.zig");48 _ = @import("behavior/bugs/624.zig");
...@@ -69,6 +73,7 @@ test {...@@ -69,6 +73,7 @@ test {
69 _ = @import("behavior/pointers.zig");73 _ = @import("behavior/pointers.zig");
70 _ = @import("behavior/ptrcast.zig");74 _ = @import("behavior/ptrcast.zig");
71 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");75 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
76 _ = @import("behavior/slice.zig");
72 _ = @import("behavior/src.zig");77 _ = @import("behavior/src.zig");
73 _ = @import("behavior/this.zig");78 _ = @import("behavior/this.zig");
74 _ = @import("behavior/try.zig");79 _ = @import("behavior/try.zig");
...@@ -88,11 +93,7 @@ test {...@@ -88,11 +93,7 @@ test {
8893
89 if (builtin.zig_backend != .stage2_c) {94 if (builtin.zig_backend != .stage2_c) {
90 // Tests that pass for stage1 and the llvm backend.95 // Tests that pass for stage1 and the llvm backend.
91 _ = @import("behavior/array_llvm.zig");
92 _ = @import("behavior/atomics.zig");96 _ = @import("behavior/atomics.zig");
93 _ = @import("behavior/bugs/1025.zig");
94 _ = @import("behavior/bugs/1741.zig");
95 _ = @import("behavior/bugs/1914.zig");
96 _ = @import("behavior/bugs/2578.zig");97 _ = @import("behavior/bugs/2578.zig");
97 _ = @import("behavior/bugs/3007.zig");98 _ = @import("behavior/bugs/3007.zig");
98 _ = @import("behavior/bugs/9584.zig");99 _ = @import("behavior/bugs/9584.zig");
...@@ -108,7 +109,6 @@ test {...@@ -108,7 +109,6 @@ test {
108 _ = @import("behavior/popcount.zig");109 _ = @import("behavior/popcount.zig");
109 _ = @import("behavior/saturating_arithmetic.zig");110 _ = @import("behavior/saturating_arithmetic.zig");
110 _ = @import("behavior/sizeof_and_typeof.zig");111 _ = @import("behavior/sizeof_and_typeof.zig");
111 _ = @import("behavior/slice.zig");
112 _ = @import("behavior/struct_llvm.zig");112 _ = @import("behavior/struct_llvm.zig");
113 _ = @import("behavior/switch.zig");113 _ = @import("behavior/switch.zig");
114 _ = @import("behavior/widening.zig");114 _ = @import("behavior/widening.zig");
test/behavior/array_llvm.zig+18
...@@ -7,6 +7,7 @@ var s_array: [8]Sub = undefined;...@@ -7,6 +7,7 @@ var s_array: [8]Sub = undefined;
7const Sub = struct { b: u8 };7const Sub = struct { b: u8 };
8const Str = struct { a: []Sub };8const Str = struct { a: []Sub };
9test "set global var array via slice embedded in struct" {9test "set global var array via slice embedded in struct" {
10 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10 var s = Str{ .a = s_array[0..] };11 var s = Str{ .a = s_array[0..] };
1112
12 s.a[0].b = 1;13 s.a[0].b = 1;
...@@ -19,6 +20,7 @@ test "set global var array via slice embedded in struct" {...@@ -19,6 +20,7 @@ test "set global var array via slice embedded in struct" {
19}20}
2021
21test "read/write through global variable array of struct fields initialized via array mult" {22test "read/write through global variable array of struct fields initialized via array mult" {
23 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
22 const S = struct {24 const S = struct {
23 fn doTheTest() !void {25 fn doTheTest() !void {
24 try expect(storage[0].term == 1);26 try expect(storage[0].term == 1);
...@@ -36,6 +38,7 @@ test "read/write through global variable array of struct fields initialized via...@@ -36,6 +38,7 @@ test "read/write through global variable array of struct fields initialized via
36}38}
3739
38test "implicit cast single-item pointer" {40test "implicit cast single-item pointer" {
41 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
39 try testImplicitCastSingleItemPtr();42 try testImplicitCastSingleItemPtr();
40 comptime try testImplicitCastSingleItemPtr();43 comptime try testImplicitCastSingleItemPtr();
41}44}
...@@ -52,6 +55,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {...@@ -52,6 +55,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
52}55}
5356
54test "comptime evaluating function that takes array by value" {57test "comptime evaluating function that takes array by value" {
58 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
55 const arr = [_]u8{ 1, 2 };59 const arr = [_]u8{ 1, 2 };
56 const x = comptime testArrayByValAtComptime(arr);60 const x = comptime testArrayByValAtComptime(arr);
57 const y = comptime testArrayByValAtComptime(arr);61 const y = comptime testArrayByValAtComptime(arr);
...@@ -60,12 +64,14 @@ test "comptime evaluating function that takes array by value" {...@@ -60,12 +64,14 @@ test "comptime evaluating function that takes array by value" {
60}64}
6165
62test "runtime initialize array elem and then implicit cast to slice" {66test "runtime initialize array elem and then implicit cast to slice" {
67 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
63 var two: i32 = 2;68 var two: i32 = 2;
64 const x: []const i32 = &[_]i32{two};69 const x: []const i32 = &[_]i32{two};
65 try expect(x[0] == 2);70 try expect(x[0] == 2);
66}71}
6772
68test "array literal as argument to function" {73test "array literal as argument to function" {
74 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
69 const S = struct {75 const S = struct {
70 fn entry(two: i32) !void {76 fn entry(two: i32) !void {
71 try foo(&[_]i32{ 1, 2, 3 });77 try foo(&[_]i32{ 1, 2, 3 });
...@@ -90,6 +96,7 @@ test "array literal as argument to function" {...@@ -90,6 +96,7 @@ test "array literal as argument to function" {
90}96}
9197
92test "double nested array to const slice cast in array literal" {98test "double nested array to const slice cast in array literal" {
99 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
93 const S = struct {100 const S = struct {
94 fn entry(two: i32) !void {101 fn entry(two: i32) !void {
95 const cases = [_][]const []const i32{102 const cases = [_][]const []const i32{
...@@ -147,6 +154,7 @@ test "double nested array to const slice cast in array literal" {...@@ -147,6 +154,7 @@ test "double nested array to const slice cast in array literal" {
147}154}
148155
149test "anonymous literal in array" {156test "anonymous literal in array" {
157 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
150 const S = struct {158 const S = struct {
151 const Foo = struct {159 const Foo = struct {
152 a: usize = 2,160 a: usize = 2,
...@@ -168,6 +176,7 @@ test "anonymous literal in array" {...@@ -168,6 +176,7 @@ test "anonymous literal in array" {
168}176}
169177
170test "access the null element of a null terminated array" {178test "access the null element of a null terminated array" {
179 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
171 const S = struct {180 const S = struct {
172 fn doTheTest() !void {181 fn doTheTest() !void {
173 var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' };182 var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' };
...@@ -181,6 +190,7 @@ test "access the null element of a null terminated array" {...@@ -181,6 +190,7 @@ test "access the null element of a null terminated array" {
181}190}
182191
183test "type deduction for array subscript expression" {192test "type deduction for array subscript expression" {
193 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
184 const S = struct {194 const S = struct {
185 fn doTheTest() !void {195 fn doTheTest() !void {
186 var array = [_]u8{ 0x55, 0xAA };196 var array = [_]u8{ 0x55, 0xAA };
...@@ -196,6 +206,8 @@ test "type deduction for array subscript expression" {...@@ -196,6 +206,8 @@ test "type deduction for array subscript expression" {
196206
197test "sentinel element count towards the ABI size calculation" {207test "sentinel element count towards the ABI size calculation" {
198 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO208 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
209 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
210 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
199211
200 const S = struct {212 const S = struct {
201 fn doTheTest() !void {213 fn doTheTest() !void {
...@@ -218,6 +230,8 @@ test "sentinel element count towards the ABI size calculation" {...@@ -218,6 +230,8 @@ test "sentinel element count towards the ABI size calculation" {
218230
219test "zero-sized array with recursive type definition" {231test "zero-sized array with recursive type definition" {
220 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO232 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
233 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
234 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
221235
222 const U = struct {236 const U = struct {
223 fn foo(comptime T: type, comptime n: usize) type {237 fn foo(comptime T: type, comptime n: usize) type {
...@@ -237,6 +251,7 @@ test "zero-sized array with recursive type definition" {...@@ -237,6 +251,7 @@ test "zero-sized array with recursive type definition" {
237}251}
238252
239test "type coercion of anon struct literal to array" {253test "type coercion of anon struct literal to array" {
254 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
240 const S = struct {255 const S = struct {
241 const U = union {256 const U = union {
242 a: u32,257 a: u32,
...@@ -253,6 +268,7 @@ test "type coercion of anon struct literal to array" {...@@ -253,6 +268,7 @@ test "type coercion of anon struct literal to array" {
253 try expect(arr1[2] == 54);268 try expect(arr1[2] == 54);
254269
255 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO270 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
271 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
256272
257 var x2: U = .{ .a = 42 };273 var x2: U = .{ .a = 42 };
258 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };274 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
...@@ -268,6 +284,8 @@ test "type coercion of anon struct literal to array" {...@@ -268,6 +284,8 @@ test "type coercion of anon struct literal to array" {
268284
269test "type coercion of pointer to anon struct literal to pointer to array" {285test "type coercion of pointer to anon struct literal to pointer to array" {
270 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO286 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
287 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
288 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
271289
272 const S = struct {290 const S = struct {
273 const U = union {291 const U = union {
test/behavior/bugs/1025.zig+4
...@@ -1,3 +1,5 @@...@@ -1,3 +1,5 @@
1const builtin = @import("builtin");
2
1const A = struct {3const A = struct {
2 B: type,4 B: type,
3};5};
...@@ -7,6 +9,8 @@ fn getA() A {...@@ -7,6 +9,8 @@ fn getA() A {
7}9}
810
9test "bug 1025" {11test "bug 1025" {
12 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10 const a = getA();14 const a = getA();
11 try @import("std").testing.expect(a.B == u8);15 try @import("std").testing.expect(a.B == u8);
12}16}
test/behavior/bugs/1741.zig+3
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
23
3test "fixed" {4test "fixed" {
5 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
4 const x: f32 align(128) = 12.34;7 const x: f32 align(128) = 12.34;
5 try std.testing.expect(@ptrToInt(&x) % 128 == 0);8 try std.testing.expect(@ptrToInt(&x) % 128 == 0);
6}9}
test/behavior/bugs/1914.zig+7
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
23
3const A = struct {4const A = struct {
4 b_list_pointer: *const []B,5 b_list_pointer: *const []B,
...@@ -11,6 +12,9 @@ const b_list: []B = &[_]B{};...@@ -11,6 +12,9 @@ const b_list: []B = &[_]B{};
11const a = A{ .b_list_pointer = &b_list };12const a = A{ .b_list_pointer = &b_list };
1213
13test "segfault bug" {14test "segfault bug" {
15 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
14 const assert = std.debug.assert;18 const assert = std.debug.assert;
15 const obj = B{ .a_pointer = &a };19 const obj = B{ .a_pointer = &a };
16 assert(obj.a_pointer == &a); // this makes zig crash20 assert(obj.a_pointer == &a); // this makes zig crash
...@@ -27,5 +31,8 @@ pub const B2 = struct {...@@ -27,5 +31,8 @@ pub const B2 = struct {
27var b_value = B2{ .pointer_array = &[_]*A2{} };31var b_value = B2{ .pointer_array = &[_]*A2{} };
2832
29test "basic stuff" {33test "basic stuff" {
34 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
35 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
36 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
30 std.debug.assert(&b_value == &b_value);37 std.debug.assert(&b_value == &b_value);
31}38}
test/behavior/slice.zig+13
...@@ -27,6 +27,7 @@ comptime {...@@ -27,6 +27,7 @@ comptime {
27}27}
2828
29test "slicing" {29test "slicing" {
30 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
30 var array: [20]i32 = undefined;31 var array: [20]i32 = undefined;
3132
32 array[5] = 1234;33 array[5] = 1234;
...@@ -43,6 +44,7 @@ test "slicing" {...@@ -43,6 +44,7 @@ test "slicing" {
43}44}
4445
45test "const slice" {46test "const slice" {
47 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
46 comptime {48 comptime {
47 const a = "1234567890";49 const a = "1234567890";
48 try expect(a.len == 10);50 try expect(a.len == 10);
...@@ -53,6 +55,7 @@ test "const slice" {...@@ -53,6 +55,7 @@ test "const slice" {
53}55}
5456
55test "comptime slice of undefined pointer of length 0" {57test "comptime slice of undefined pointer of length 0" {
58 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
56 const slice1 = @as([*]i32, undefined)[0..0];59 const slice1 = @as([*]i32, undefined)[0..0];
57 try expect(slice1.len == 0);60 try expect(slice1.len == 0);
58 const slice2 = @as([*]i32, undefined)[100..100];61 const slice2 = @as([*]i32, undefined)[100..100];
...@@ -60,6 +63,7 @@ test "comptime slice of undefined pointer of length 0" {...@@ -60,6 +63,7 @@ test "comptime slice of undefined pointer of length 0" {
60}63}
6164
62test "implicitly cast array of size 0 to slice" {65test "implicitly cast array of size 0 to slice" {
66 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
63 var msg = [_]u8{};67 var msg = [_]u8{};
64 try assertLenIsZero(&msg);68 try assertLenIsZero(&msg);
65}69}
...@@ -69,6 +73,7 @@ fn assertLenIsZero(msg: []const u8) !void {...@@ -69,6 +73,7 @@ fn assertLenIsZero(msg: []const u8) !void {
69}73}
7074
71test "access len index of sentinel-terminated slice" {75test "access len index of sentinel-terminated slice" {
76 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
72 const S = struct {77 const S = struct {
73 fn doTheTest() !void {78 fn doTheTest() !void {
74 var slice: [:0]const u8 = "hello";79 var slice: [:0]const u8 = "hello";
...@@ -82,6 +87,7 @@ test "access len index of sentinel-terminated slice" {...@@ -82,6 +87,7 @@ test "access len index of sentinel-terminated slice" {
82}87}
8388
84test "comptime slice of slice preserves comptime var" {89test "comptime slice of slice preserves comptime var" {
90 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
85 comptime {91 comptime {
86 var buff: [10]u8 = undefined;92 var buff: [10]u8 = undefined;
87 buff[0..][0..][0] = 1;93 buff[0..][0..][0] = 1;
...@@ -90,6 +96,7 @@ test "comptime slice of slice preserves comptime var" {...@@ -90,6 +96,7 @@ test "comptime slice of slice preserves comptime var" {
90}96}
9197
92test "slice of type" {98test "slice of type" {
99 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
93 comptime {100 comptime {
94 var types_array = [_]type{ i32, f64, type };101 var types_array = [_]type{ i32, f64, type };
95 for (types_array) |T, i| {102 for (types_array) |T, i| {
...@@ -112,6 +119,7 @@ test "slice of type" {...@@ -112,6 +119,7 @@ test "slice of type" {
112}119}
113120
114test "generic malloc free" {121test "generic malloc free" {
122 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
115 const a = memAlloc(u8, 10) catch unreachable;123 const a = memAlloc(u8, 10) catch unreachable;
116 memFree(u8, a);124 memFree(u8, a);
117}125}
...@@ -124,6 +132,7 @@ fn memFree(comptime T: type, memory: []T) void {...@@ -124,6 +132,7 @@ fn memFree(comptime T: type, memory: []T) void {
124}132}
125133
126test "slice of hardcoded address to pointer" {134test "slice of hardcoded address to pointer" {
135 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
127 const S = struct {136 const S = struct {
128 fn doTheTest() !void {137 fn doTheTest() !void {
129 const pointer = @intToPtr([*]u8, 0x04)[0..2];138 const pointer = @intToPtr([*]u8, 0x04)[0..2];
...@@ -138,6 +147,7 @@ test "slice of hardcoded address to pointer" {...@@ -138,6 +147,7 @@ test "slice of hardcoded address to pointer" {
138}147}
139148
140test "comptime slice of pointer preserves comptime var" {149test "comptime slice of pointer preserves comptime var" {
150 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
141 comptime {151 comptime {
142 var buff: [10]u8 = undefined;152 var buff: [10]u8 = undefined;
143 var a = @ptrCast([*]u8, &buff);153 var a = @ptrCast([*]u8, &buff);
...@@ -147,6 +157,7 @@ test "comptime slice of pointer preserves comptime var" {...@@ -147,6 +157,7 @@ test "comptime slice of pointer preserves comptime var" {
147}157}
148158
149test "comptime pointer cast array and then slice" {159test "comptime pointer cast array and then slice" {
160 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
150 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };161 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
151162
152 const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);163 const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
...@@ -160,6 +171,7 @@ test "comptime pointer cast array and then slice" {...@@ -160,6 +171,7 @@ test "comptime pointer cast array and then slice" {
160}171}
161172
162test "slicing zero length array" {173test "slicing zero length array" {
174 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
163 const s1 = ""[0..];175 const s1 = ""[0..];
164 const s2 = ([_]u32{})[0..];176 const s2 = ([_]u32{})[0..];
165 try expect(s1.len == 0);177 try expect(s1.len == 0);
...@@ -171,6 +183,7 @@ test "slicing zero length array" {...@@ -171,6 +183,7 @@ test "slicing zero length array" {
171const x = @intToPtr([*]i32, 0x1000)[0..0x500];183const x = @intToPtr([*]i32, 0x1000)[0..0x500];
172const y = x[0x100..];184const y = x[0x100..];
173test "compile time slice of pointer to hard coded address" {185test "compile time slice of pointer to hard coded address" {
186 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
174 if (builtin.zig_backend == .stage1) return error.SkipZigTest;187 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
175188
176 try expect(@ptrToInt(x) == 0x1000);189 try expect(@ptrToInt(x) == 0x1000);