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) {
3939 /// Note: The value contains the symbol index, rather than the actual address
4040 /// as we use this to perform the relocation.
4141 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 },
4250 /// Represents a function pointer
4351 /// In wasm function pointers are indexes into a function table,
4452 /// rather than an address in the data section.
......@@ -552,6 +560,9 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{},
552560/// When a function is executing, we store the the current stack pointer's value within this local.
553561/// This value is then used to restore the stack pointer to the original value at the return of the function.
554562initial_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,
555566/// Arguments of this function declaration
556567/// This will be set after `resolveCallingConventionValues`
557568args: []WValue = &.{},
......@@ -559,6 +570,14 @@ args: []WValue = &.{},
559570/// When it returns a pointer to the stack, the `.local` tag will be active and must be populated
560571/// before this function returns its execution to the caller.
561572return_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
563582const InnerError = error{
564583 OutOfMemory,
......@@ -598,7 +617,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
598617 // means we must generate it from a constant.
599618 const val = self.air.value(ref).?;
600619 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
603625 // When we need to pass the value by reference (such as a struct), we will
604626 // 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 {
643665 try self.mir_instructions.append(self.gpa, inst);
644666}
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
653668fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
654669 try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
655670}
......@@ -754,7 +769,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void {
754769 .imm64 => |val| try self.addImm64(val),
755770 .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),
756771 .float64 => |val| try self.addFloat64(val),
757 .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation
772 .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 },
758780 .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation
759781 }
760782}
......@@ -827,10 +849,43 @@ pub fn genFunc(self: *Self) InnerError!void {
827849 try self.addTag(.@"unreachable");
828850 }
829851 }
830
831852 // End of function body
832853 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
834889 var mir: Mir = .{
835890 .instructions = self.mir_instructions.toOwnedSlice(),
836891 .extra = self.mir_extra.toOwnedSlice(self.gpa),
......@@ -927,7 +982,7 @@ pub const DeclGen = struct {
927982 .function => val.castTag(.function).?.data.owner_decl,
928983 else => unreachable,
929984 };
930 return try self.lowerDeclRef(ty, val, fn_decl);
985 return try self.lowerDeclRefValue(ty, val, fn_decl, 0);
931986 },
932987 .Optional => {
933988 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -1115,11 +1170,11 @@ pub const DeclGen = struct {
11151170 .Pointer => switch (val.tag()) {
11161171 .variable => {
11171172 const decl = val.castTag(.variable).?.data.owner_decl;
1118 return self.lowerDeclRef(ty, val, decl);
1173 return self.lowerDeclRefValue(ty, val, decl, 0);
11191174 },
11201175 .decl_ref => {
11211176 const decl = val.castTag(.decl_ref).?.data;
1122 return self.lowerDeclRef(ty, val, decl);
1177 return self.lowerDeclRefValue(ty, val, decl, 0);
11231178 },
11241179 .slice => {
11251180 const slice = val.castTag(.slice).?.data;
......@@ -1139,6 +1194,13 @@ pub const DeclGen = struct {
11391194 try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8));
11401195 return Result{ .appended = {} };
11411196 },
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),
11421204 else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}),
11431205 },
11441206 .ErrorUnion => {
......@@ -1179,7 +1241,36 @@ pub const DeclGen = struct {
11791241 }
11801242 }
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 {
11831274 const writer = self.code.writer();
11841275 if (ty.isSlice()) {
11851276 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
......@@ -1202,6 +1293,7 @@ pub const DeclGen = struct {
12021293 self.symbol_index, // source symbol index
12031294 decl.link.wasm.sym_index, // target symbol index
12041295 @intCast(u32, self.code.items.len), // offset
1296 @intCast(u32, offset), // addend
12051297 ));
12061298 return Result{ .appended = {} };
12071299 }
......@@ -1254,22 +1346,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu
12541346 return result;
12551347}
12561348
1257/// Retrieves the stack pointer's value from the global variable and stores
1258/// it in a local
1349/// Creates a local for the initial stack value
12591350/// Asserts `initial_stack_value` is `.none`
12601351fn initializeStack(self: *Self) !void {
12611352 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
12661353 // Reserve a local to store the current stack pointer
12671354 // We can later use this local to set the stack pointer back to the value
12681355 // we have stored here.
1269 self.initial_stack_value = try self.allocLocal(Type.initTag(.i32));
1270
1271 // save the value to the local
1272 try self.addLabel(.local_set, self.initial_stack_value.local);
1356 self.initial_stack_value = try self.allocLocal(Type.usize);
1357 // Also reserve a local to store the bottom stack value
1358 self.bottom_stack_value = try self.allocLocal(Type.usize);
12731359}
12741360
12751361/// Reads the stack pointer from `Context.initial_stack_value` and writes it
......@@ -1284,36 +1370,75 @@ fn restoreStackPointer(self: *Self) !void {
12841370 try self.addLabel(.global_set, 0);
12851371}
12861372
1287/// Moves the stack pointer by given `offset`
1288/// It does this by retrieving the stack pointer, subtracting `offset` and storing
1289/// the result back into the stack pointer.
1290fn moveStack(self: *Self, offset: u32, local: u32) !void {
1291 if (offset == 0) return;
1292 try self.addLabel(.global_get, 0);
1293 try self.addImm32(@bitCast(i32, offset));
1294 try self.addTag(.i32_sub);
1295 try self.addLabel(.local_tee, local);
1296 try self.addLabel(.global_set, 0);
1373/// Saves the current stack size's stack pointer position into a given local
1374/// It does this by retrieving the bottom stack pointer, adding `self.stack_size` and storing
1375/// the result back into the local.
1376fn saveStack(self: *Self) !WValue {
1377 const local = try self.allocLocal(Type.usize);
1378 try self.addLabel(.local_get, self.bottom_stack_value.local);
1379 try self.addImm32(@intCast(i32, self.stack_size));
1380 try self.addTag(.i32_add);
1381 try self.addLabel(.local_set, local.local);
1382 return local;
12971383}
12981384
12991385/// From a given type, will create space on the virtual stack to store the value of such type.
13001386/// This returns a `WValue` with its active tag set to `local`, containing the index to the local
13011387/// 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.
13031389///
13041390/// Asserts Type has codegenbits
13051391fn allocStack(self: *Self, ty: Type) !WValue {
13061392 assert(ty.hasRuntimeBits());
1393 if (self.initial_stack_value == .none) {
1394 try self.initializeStack();
1395 }
13071396
1308 // calculate needed stack space
13091397 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) });
13111399 };
1400 const abi_align = ty.abiAlignment(self.target);
13121401
1313 // allocate a local using wasm's pointer size
1314 const local = try self.allocLocal(Type.@"usize");
1315 try self.moveStack(abi_size, local.local);
1316 return local;
1402 if (abi_align > self.stack_alignment) {
1403 self.stack_alignment = abi_align;
1404 }
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();
13171442}
13181443
13191444/// From given zig bitsize, returns the wasm bitsize
......@@ -1592,6 +1717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
15921717fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
15931718 const un_op = self.air.instructions.items(.data)[inst].un_op;
15941719 const operand = try self.resolveInst(un_op);
1720
15951721 // result must be stored in the stack and we return a pointer
15961722 // to the stack instead
15971723 if (self.return_value != .none) {
......@@ -1601,7 +1727,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16011727 }
16021728 try self.restoreStackPointer();
16031729 try self.addTag(.@"return");
1604 return .none;
1730 return WValue{ .none = {} };
16051731}
16061732
16071733fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1611,12 +1737,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
16111737 if (isByRef(child_type, self.target)) {
16121738 return self.return_value;
16131739 }
1614
1615 // Initialize the stack
1616 if (self.initial_stack_value == .none) {
1617 try self.initializeStack();
1618 }
1619 return self.allocStack(child_type);
1740 return self.allocStackPtr(inst);
16201741}
16211742
16221743fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -1708,20 +1829,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
17081829}
17091830
17101831fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
1711 const pointee_type = self.air.typeOfIndex(inst).childType();
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);
1832 return self.allocStackPtr(inst);
17251833}
17261834
17271835fn 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
17411849 const err_ty = ty.errorUnionSet();
17421850 const pl_ty = ty.errorUnionPayload();
17431851 if (!pl_ty.hasRuntimeBits()) {
1744 const err_val = try self.load(rhs, err_ty, 0);
1745 return self.store(lhs, err_val, err_ty, 0);
1852 return self.store(lhs, rhs, err_ty, 0);
17461853 }
17471854
1748 return try self.memCopy(ty, lhs, rhs);
1855 return self.memCopy(ty, lhs, rhs);
17491856 },
17501857 .Optional => {
17511858 if (ty.isPtrLikeOptional()) {
......@@ -1760,7 +1867,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17601867 return self.memCopy(ty, lhs, rhs);
17611868 },
17621869 .Struct, .Array, .Union => {
1763 return try self.memCopy(ty, lhs, rhs);
1870 return self.memCopy(ty, lhs, rhs);
17641871 },
17651872 .Pointer => {
17661873 if (ty.isSlice()) {
......@@ -1775,7 +1882,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro
17751882 }
17761883 },
17771884 .Int => if (ty.intInfo(self.target).bits > 64) {
1778 return try self.memCopy(ty, lhs, rhs);
1885 return self.memCopy(ty, lhs, rhs);
17791886 },
17801887 else => {},
17811888 }
......@@ -1974,6 +2081,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
19742081 return WValue{ .function_index = target_sym_index };
19752082 } else return WValue{ .memory = target_sym_index };
19762083 },
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 },
19772095 .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) },
19782096 .zero, .null_value => return WValue{ .imm32 = 0 },
19792097 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
25242642 if (isByRef(payload_ty, self.target)) {
25252643 return self.buildPointerOffset(operand, offset, .new);
25262644 }
2527 return try self.load(operand, payload_ty, offset);
2645 return self.load(operand, payload_ty, offset);
25282646}
25292647
25302648fn 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
25332651 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
25342652 const operand = try self.resolveInst(ty_op.operand);
......@@ -2538,11 +2656,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
25382656 return operand;
25392657 }
25402658
2541 return try self.load(operand, err_ty.errorUnionSet(), 0);
2659 return self.load(operand, err_ty.errorUnionSet(), 0);
25422660}
25432661
25442662fn 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
25462665 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
25472666 const operand = try self.resolveInst(ty_op.operand);
25482667
......@@ -2564,11 +2683,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
25642683}
25652684
25662685fn 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
25682688 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
25692689 const operand = try self.resolveInst(ty_op.operand);
25702690 const err_ty = self.air.getRefType(ty_op.ty);
25712691
2692 if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand;
2693
25722694 const err_union = try self.allocStack(err_ty);
25732695 // TODO: Also write 'undefined' to the payload
25742696 try self.store(err_union, operand, err_ty.errorUnionSet(), 0);
......@@ -2750,16 +2872,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27502872}
27512873
27522874fn 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
27552877 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
27562878 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());
27592881}
27602882
27612883fn 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
27642886 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
27652887 const slice_ty = self.air.typeOf(bin_op.lhs);
......@@ -2784,7 +2906,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
27842906 if (isByRef(elem_ty, self.target)) {
27852907 return result;
27862908 }
2787 return try self.load(result, elem_ty, 0);
2909 return self.load(result, elem_ty, 0);
27882910}
27892911
27902912fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2812,10 +2934,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28122934}
28132935
28142936fn 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 = {} };
28162938 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
28172939 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);
28192941}
28202942
28212943fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2880,7 +3002,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28803002
28813003fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
28823004 const un_op = self.air.instructions.items(.data)[inst].un_op;
2883 return try self.resolveInst(un_op);
3005 return self.resolveInst(un_op);
28843006}
28853007
28863008fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2912,7 +3034,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29123034fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29133035 if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
29143036 const un_op = self.air.instructions.items(.data)[inst].un_op;
2915 return try self.resolveInst(un_op);
3037 return self.resolveInst(un_op);
29163038}
29173039
29183040fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2927,7 +3049,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29273049
29283050 // load pointer onto the stack
29293051 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);
29313053 try self.addLabel(.local_get, ptr_local.local);
29323054 } else {
29333055 try self.emitWValue(pointer);
......@@ -2944,7 +3066,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29443066 if (isByRef(elem_ty, self.target)) {
29453067 return result;
29463068 }
2947 return try self.load(result, elem_ty, 0);
3069 return self.load(result, elem_ty, 0);
29483070}
29493071
29503072fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -2960,7 +3082,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
29603082
29613083 // load pointer onto the stack
29623084 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);
29643086 try self.addLabel(.local_get, ptr_local.local);
29653087 } else {
29663088 try self.emitWValue(ptr);
......@@ -3094,7 +3216,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
30943216 if (isByRef(elem_ty, self.target)) {
30953217 return result;
30963218 }
3097 return try self.load(result, elem_ty, 0);
3219 return self.load(result, elem_ty, 0);
30983220}
30993221
31003222fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
......@@ -3138,8 +3260,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
31383260 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
31393261 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
31403262
3141 _ = elements;
3142 return self.fail("TODO: Wasm backend: implement airVectorInit", .{});
3263 switch (vector_ty.zigTypeTag()) {
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 }
31433320}
31443321
31453322fn 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 {
326326}
327327
328328fn 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;
330331 const mem_offset = emit.offset() + 1;
331332 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;
332333 if (is_wasm32) {
333334 try emit.code.append(std.wasm.opcode(.i32_const));
334335 var buf: [5]u8 = undefined;
335 leb128.writeUnsignedFixed(5, &buf, symbol_index);
336 leb128.writeUnsignedFixed(5, &buf, mem.pointer);
336337 try emit.code.appendSlice(&buf);
337338 } else {
338339 try emit.code.append(std.wasm.opcode(.i64_const));
339340 var buf: [10]u8 = undefined;
340 leb128.writeUnsignedFixed(10, &buf, symbol_index);
341 leb128.writeUnsignedFixed(10, &buf, mem.pointer);
341342 try emit.code.appendSlice(&buf);
342343 }
343344
344345 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
345346 .offset = mem_offset,
346 .index = symbol_index,
347 .index = mem.pointer,
347348 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
349 .addend = mem.offset,
348350 });
349351}
350352
src/arch/wasm/Mir.zig+7
......@@ -546,3 +546,10 @@ pub const MemArg = struct {
546546 offset: u32,
547547 alignment: u32,
548548};
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,
345345
346346/// For a given decl, find the given symbol index's atom, and create a relocation for the type.
347347/// 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 {
349357 const atom = decl.link.wasm.symbolAtom(symbol_index);
350358 const is_wasm32 = self.base.options.target.cpu.arch == .wasm32;
351359 if (ty.zigTypeTag() == .Fn) {
360 std.debug.assert(addend == 0); // addend not allowed for function relocations
352361 // We found a function pointer, so add it to our table,
353362 // as function pointers are not allowed to be stored inside the data section.
354363 // 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
363372 .index = target_symbol_index,
364373 .offset = offset,
365374 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64,
375 .addend = addend,
366376 });
367377 }
368378 // we do not know the final address at this point,
test/behavior.zig+6-6
......@@ -10,6 +10,7 @@ test {
1010 _ = @import("behavior/bugs/655.zig");
1111 _ = @import("behavior/bugs/656.zig");
1212 _ = @import("behavior/bugs/679.zig");
13 _ = @import("behavior/bugs/1025.zig");
1314 _ = @import("behavior/bugs/1111.zig");
1415 _ = @import("behavior/bugs/1277.zig");
1516 _ = @import("behavior/bugs/1310.zig");
......@@ -17,6 +18,8 @@ test {
1718 _ = @import("behavior/bugs/1486.zig");
1819 _ = @import("behavior/bugs/1500.zig");
1920 _ = @import("behavior/bugs/1735.zig");
21 _ = @import("behavior/bugs/1741.zig");
22 _ = @import("behavior/bugs/1914.zig");
2023 _ = @import("behavior/bugs/2006.zig");
2124 _ = @import("behavior/bugs/2346.zig");
2225 _ = @import("behavior/bugs/3112.zig");
......@@ -38,7 +41,8 @@ test {
3841 _ = @import("behavior/struct.zig");
3942
4043 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");
4246 _ = @import("behavior/basic.zig");
4347 _ = @import("behavior/bitcast.zig");
4448 _ = @import("behavior/bugs/624.zig");
......@@ -69,6 +73,7 @@ test {
6973 _ = @import("behavior/pointers.zig");
7074 _ = @import("behavior/ptrcast.zig");
7175 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
76 _ = @import("behavior/slice.zig");
7277 _ = @import("behavior/src.zig");
7378 _ = @import("behavior/this.zig");
7479 _ = @import("behavior/try.zig");
......@@ -88,11 +93,7 @@ test {
8893
8994 if (builtin.zig_backend != .stage2_c) {
9095 // Tests that pass for stage1 and the llvm backend.
91 _ = @import("behavior/array_llvm.zig");
9296 _ = @import("behavior/atomics.zig");
93 _ = @import("behavior/bugs/1025.zig");
94 _ = @import("behavior/bugs/1741.zig");
95 _ = @import("behavior/bugs/1914.zig");
9697 _ = @import("behavior/bugs/2578.zig");
9798 _ = @import("behavior/bugs/3007.zig");
9899 _ = @import("behavior/bugs/9584.zig");
......@@ -108,7 +109,6 @@ test {
108109 _ = @import("behavior/popcount.zig");
109110 _ = @import("behavior/saturating_arithmetic.zig");
110111 _ = @import("behavior/sizeof_and_typeof.zig");
111 _ = @import("behavior/slice.zig");
112112 _ = @import("behavior/struct_llvm.zig");
113113 _ = @import("behavior/switch.zig");
114114 _ = @import("behavior/widening.zig");
test/behavior/array_llvm.zig+18
......@@ -7,6 +7,7 @@ var s_array: [8]Sub = undefined;
77const Sub = struct { b: u8 };
88const Str = struct { a: []Sub };
99test "set global var array via slice embedded in struct" {
10 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1011 var s = Str{ .a = s_array[0..] };
1112
1213 s.a[0].b = 1;
......@@ -19,6 +20,7 @@ test "set global var array via slice embedded in struct" {
1920}
2021
2122test "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
2224 const S = struct {
2325 fn doTheTest() !void {
2426 try expect(storage[0].term == 1);
......@@ -36,6 +38,7 @@ test "read/write through global variable array of struct fields initialized via
3638}
3739
3840test "implicit cast single-item pointer" {
41 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
3942 try testImplicitCastSingleItemPtr();
4043 comptime try testImplicitCastSingleItemPtr();
4144}
......@@ -52,6 +55,7 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
5255}
5356
5457test "comptime evaluating function that takes array by value" {
58 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
5559 const arr = [_]u8{ 1, 2 };
5660 const x = comptime testArrayByValAtComptime(arr);
5761 const y = comptime testArrayByValAtComptime(arr);
......@@ -60,12 +64,14 @@ test "comptime evaluating function that takes array by value" {
6064}
6165
6266test "runtime initialize array elem and then implicit cast to slice" {
67 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
6368 var two: i32 = 2;
6469 const x: []const i32 = &[_]i32{two};
6570 try expect(x[0] == 2);
6671}
6772
6873test "array literal as argument to function" {
74 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
6975 const S = struct {
7076 fn entry(two: i32) !void {
7177 try foo(&[_]i32{ 1, 2, 3 });
......@@ -90,6 +96,7 @@ test "array literal as argument to function" {
9096}
9197
9298test "double nested array to const slice cast in array literal" {
99 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
93100 const S = struct {
94101 fn entry(two: i32) !void {
95102 const cases = [_][]const []const i32{
......@@ -147,6 +154,7 @@ test "double nested array to const slice cast in array literal" {
147154}
148155
149156test "anonymous literal in array" {
157 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
150158 const S = struct {
151159 const Foo = struct {
152160 a: usize = 2,
......@@ -168,6 +176,7 @@ test "anonymous literal in array" {
168176}
169177
170178test "access the null element of a null terminated array" {
179 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
171180 const S = struct {
172181 fn doTheTest() !void {
173182 var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' };
......@@ -181,6 +190,7 @@ test "access the null element of a null terminated array" {
181190}
182191
183192test "type deduction for array subscript expression" {
193 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
184194 const S = struct {
185195 fn doTheTest() !void {
186196 var array = [_]u8{ 0x55, 0xAA };
......@@ -196,6 +206,8 @@ test "type deduction for array subscript expression" {
196206
197207test "sentinel element count towards the ABI size calculation" {
198208 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
200212 const S = struct {
201213 fn doTheTest() !void {
......@@ -218,6 +230,8 @@ test "sentinel element count towards the ABI size calculation" {
218230
219231test "zero-sized array with recursive type definition" {
220232 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
222236 const U = struct {
223237 fn foo(comptime T: type, comptime n: usize) type {
......@@ -237,6 +251,7 @@ test "zero-sized array with recursive type definition" {
237251}
238252
239253test "type coercion of anon struct literal to array" {
254 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest; // TODO
240255 const S = struct {
241256 const U = union {
242257 a: u32,
......@@ -253,6 +268,7 @@ test "type coercion of anon struct literal to array" {
253268 try expect(arr1[2] == 54);
254269
255270 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
271 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
256272
257273 var x2: U = .{ .a = 42 };
258274 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
......@@ -268,6 +284,8 @@ test "type coercion of anon struct literal to array" {
268284
269285test "type coercion of pointer to anon struct literal to pointer to array" {
270286 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
272290 const S = struct {
273291 const U = union {
test/behavior/bugs/1025.zig+4
......@@ -1,3 +1,5 @@
1const builtin = @import("builtin");
2
13const A = struct {
24 B: type,
35};
......@@ -7,6 +9,8 @@ fn getA() A {
79}
810
911test "bug 1025" {
12 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1014 const a = getA();
1115 try @import("std").testing.expect(a.B == u8);
1216}
test/behavior/bugs/1741.zig+3
......@@ -1,6 +1,9 @@
11const std = @import("std");
2const builtin = @import("builtin");
23
34test "fixed" {
5 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
47 const x: f32 align(128) = 12.34;
58 try std.testing.expect(@ptrToInt(&x) % 128 == 0);
69}
test/behavior/bugs/1914.zig+7
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23
34const A = struct {
45 b_list_pointer: *const []B,
......@@ -11,6 +12,9 @@ const b_list: []B = &[_]B{};
1112const a = A{ .b_list_pointer = &b_list };
1213
1314test "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;
1418 const assert = std.debug.assert;
1519 const obj = B{ .a_pointer = &a };
1620 assert(obj.a_pointer == &a); // this makes zig crash
......@@ -27,5 +31,8 @@ pub const B2 = struct {
2731var b_value = B2{ .pointer_array = &[_]*A2{} };
2832
2933test "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
3037 std.debug.assert(&b_value == &b_value);
3138}
test/behavior/slice.zig+13
......@@ -27,6 +27,7 @@ comptime {
2727}
2828
2929test "slicing" {
30 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
3031 var array: [20]i32 = undefined;
3132
3233 array[5] = 1234;
......@@ -43,6 +44,7 @@ test "slicing" {
4344}
4445
4546test "const slice" {
47 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
4648 comptime {
4749 const a = "1234567890";
4850 try expect(a.len == 10);
......@@ -53,6 +55,7 @@ test "const slice" {
5355}
5456
5557test "comptime slice of undefined pointer of length 0" {
58 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
5659 const slice1 = @as([*]i32, undefined)[0..0];
5760 try expect(slice1.len == 0);
5861 const slice2 = @as([*]i32, undefined)[100..100];
......@@ -60,6 +63,7 @@ test "comptime slice of undefined pointer of length 0" {
6063}
6164
6265test "implicitly cast array of size 0 to slice" {
66 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
6367 var msg = [_]u8{};
6468 try assertLenIsZero(&msg);
6569}
......@@ -69,6 +73,7 @@ fn assertLenIsZero(msg: []const u8) !void {
6973}
7074
7175test "access len index of sentinel-terminated slice" {
76 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
7277 const S = struct {
7378 fn doTheTest() !void {
7479 var slice: [:0]const u8 = "hello";
......@@ -82,6 +87,7 @@ test "access len index of sentinel-terminated slice" {
8287}
8388
8489test "comptime slice of slice preserves comptime var" {
90 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
8591 comptime {
8692 var buff: [10]u8 = undefined;
8793 buff[0..][0..][0] = 1;
......@@ -90,6 +96,7 @@ test "comptime slice of slice preserves comptime var" {
9096}
9197
9298test "slice of type" {
99 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
93100 comptime {
94101 var types_array = [_]type{ i32, f64, type };
95102 for (types_array) |T, i| {
......@@ -112,6 +119,7 @@ test "slice of type" {
112119}
113120
114121test "generic malloc free" {
122 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
115123 const a = memAlloc(u8, 10) catch unreachable;
116124 memFree(u8, a);
117125}
......@@ -124,6 +132,7 @@ fn memFree(comptime T: type, memory: []T) void {
124132}
125133
126134test "slice of hardcoded address to pointer" {
135 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
127136 const S = struct {
128137 fn doTheTest() !void {
129138 const pointer = @intToPtr([*]u8, 0x04)[0..2];
......@@ -138,6 +147,7 @@ test "slice of hardcoded address to pointer" {
138147}
139148
140149test "comptime slice of pointer preserves comptime var" {
150 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
141151 comptime {
142152 var buff: [10]u8 = undefined;
143153 var a = @ptrCast([*]u8, &buff);
......@@ -147,6 +157,7 @@ test "comptime slice of pointer preserves comptime var" {
147157}
148158
149159test "comptime pointer cast array and then slice" {
160 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
150161 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
151162
152163 const ptrA: [*]const u8 = @ptrCast([*]const u8, &array);
......@@ -160,6 +171,7 @@ test "comptime pointer cast array and then slice" {
160171}
161172
162173test "slicing zero length array" {
174 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
163175 const s1 = ""[0..];
164176 const s2 = ([_]u32{})[0..];
165177 try expect(s1.len == 0);
......@@ -171,6 +183,7 @@ test "slicing zero length array" {
171183const x = @intToPtr([*]i32, 0x1000)[0..0x500];
172184const y = x[0x100..];
173185test "compile time slice of pointer to hard coded address" {
186 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
174187 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
175188
176189 try expect(@ptrToInt(x) == 0x1000);