| ... | ... | @@ -1338,11 +1338,21 @@ const SimdStoreStrategy = enum { |
| 1338 | 1338 | /// it using a instruction, rather than an unrolled version. |
| 1339 | 1339 | pub fn determineSimdStoreStrategy(ty: Type, zcu: *const Zcu, target: *const std.Target) SimdStoreStrategy { |
| 1340 | 1340 | assert(ty.zigTypeTag(zcu) == .vector); |
| 1341 | | if (ty.bitSize(zcu) != 128) return .unrolled; |
| 1342 | | if (target.cpu.has(.wasm, .relaxed_simd) or target.cpu.has(.wasm, .simd128)) { |
| 1343 | | return .direct; |
| 1344 | | } |
| 1345 | | return .unrolled; |
| 1341 | if (!target.cpu.has(.wasm, .simd128)) return .unrolled; |
| 1342 | return switch (ty.toIntern()) { |
| 1343 | .vector_16_i8_type, |
| 1344 | .vector_16_u8_type, |
| 1345 | .vector_8_i16_type, |
| 1346 | .vector_8_u16_type, |
| 1347 | .vector_4_i32_type, |
| 1348 | .vector_4_u32_type, |
| 1349 | .vector_2_i64_type, |
| 1350 | .vector_2_u64_type, |
| 1351 | .vector_4_f32_type, |
| 1352 | .vector_2_f64_type, |
| 1353 | => .direct, |
| 1354 | else => .unrolled, |
| 1355 | }; |
| 1346 | 1356 | } |
| 1347 | 1357 | |
| 1348 | 1358 | fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -5188,12 +5198,27 @@ fn lowerConstant(cg: *CodeGen, val: Value) InnerError!WValue { |
| 5188 | 5198 | } else { |
| 5189 | 5199 | return .{ .imm32 = @intFromBool(!val.isNull(zcu)) }; |
| 5190 | 5200 | }, |
| 5191 | | .aggregate => switch (ip.indexToKey(ty.ip_index)) { |
| 5192 | | .array_type => return cg.fail("Wasm TODO: LowerConstant for {f}", .{ty.fmt(zcu)}), |
| 5193 | | .vector_type => { |
| 5201 | .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { |
| 5202 | .array_type => unreachable, |
| 5203 | .vector_type => |vector_type| { |
| 5194 | 5204 | assert(determineSimdStoreStrategy(ty, zcu, cg.target) == .direct); |
| 5195 | 5205 | var buf: [16]u8 = undefined; |
| 5196 | | val.writeToMemory(zcu, &buf) catch unreachable; |
| 5206 | const elem_size: usize = @intCast(Type.fromInterned(vector_type.child).abiSize(zcu)); |
| 5207 | for (0..vector_type.len) |i| { |
| 5208 | const dest = buf[i * elem_size ..][0..elem_size]; |
| 5209 | const elem: Value = switch (aggregate.storage) { |
| 5210 | .bytes => |bytes| { |
| 5211 | dest[0] = bytes.at(i, ip); |
| 5212 | continue; |
| 5213 | }, |
| 5214 | .elems => |elems| .fromInterned(elems[i]), |
| 5215 | .repeated_elem => |elem| .fromInterned(elem), |
| 5216 | }; |
| 5217 | elem.writeToMemory(zcu, dest) catch |err| switch (err) { |
| 5218 | error.OutOfMemory => return error.OutOfMemory, |
| 5219 | error.ReinterpretDeclRef, error.IllDefinedMemoryLayout => unreachable, |
| 5220 | }; |
| 5221 | } |
| 5197 | 5222 | return cg.storeSimdImmd(buf); |
| 5198 | 5223 | }, |
| 5199 | 5224 | .struct_type => unreachable, // packed structs use `bitpack` |
| ... | ... | @@ -5216,6 +5241,7 @@ fn storeSimdImmd(cg: *CodeGen, value: [16]u8) !WValue { |
| 5216 | 5241 | fn emitUndefined(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 5217 | 5242 | const zcu = cg.pt.zcu; |
| 5218 | 5243 | switch (ty.zigTypeTag(zcu)) { |
| 5244 | .vector => return cg.storeSimdImmd(@splat(0xaa)), |
| 5219 | 5245 | .bool, .error_set => return .{ .imm32 = 0xaaaaaaaa }, |
| 5220 | 5246 | .int, .@"enum" => switch (ty.intInfo(zcu).bits) { |
| 5221 | 5247 | 0...32 => return .{ .imm32 = 0xaaaaaaaa }, |
| ... | ... | @@ -5785,7 +5811,7 @@ fn bitcast(cg: *CodeGen, dest_ty: Type, src_ty: Type, operand: WValue) InnerErro |
| 5785 | 5811 | .f80, .f128 => unreachable, |
| 5786 | 5812 | }, |
| 5787 | 5813 | .int => {}, |
| 5788 | | .aggregate => unreachable, |
| 5814 | .aggregate => return operand, // both v128 |
| 5789 | 5815 | } |
| 5790 | 5816 | |
| 5791 | 5817 | switch (dest_class) { |
| ... | ... | @@ -6991,22 +7017,49 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6991 | 7017 | const result: WValue = result_value: { |
| 6992 | 7018 | switch (result_ty.zigTypeTag(zcu)) { |
| 6993 | 7019 | .array, .vector => { |
| 6994 | | const result = try cg.allocStack(result_ty); |
| 6995 | 7020 | const elem_ty = result_ty.childType(zcu); |
| 6996 | | const elem_size = elem_ty.abiSize(zcu); |
| 6997 | | const sentinel = result_ty.sentinel(zcu); |
| 6998 | | |
| 6999 | | var offset: u64 = 0; |
| 7000 | | for (elements) |elem| { |
| 7001 | | const elem_val = try cg.resolveInst(elem); |
| 7002 | | try cg.store(result, elem_val, elem_ty, .{ .offset = offset }); |
| 7003 | | offset += elem_size; |
| 7004 | | } |
| 7005 | | if (sentinel) |s| { |
| 7006 | | const val = try cg.resolveValue(s); |
| 7007 | | try cg.store(result, val, elem_ty, .{ .offset = offset }); |
| 7021 | if (isByRef(result_ty, zcu, cg.target)) { |
| 7022 | const result = try cg.allocStack(result_ty); |
| 7023 | const elem_size = elem_ty.abiSize(zcu); |
| 7024 | const sentinel = result_ty.sentinel(zcu); |
| 7025 | |
| 7026 | var offset: u64 = 0; |
| 7027 | for (elements) |elem| { |
| 7028 | const elem_val = try cg.resolveInst(elem); |
| 7029 | try cg.store(result, elem_val, elem_ty, .{ .offset = offset }); |
| 7030 | offset += elem_size; |
| 7031 | } |
| 7032 | if (sentinel) |s| { |
| 7033 | const val = try cg.resolveValue(s); |
| 7034 | try cg.store(result, val, elem_ty, .{ .offset = offset }); |
| 7035 | } |
| 7036 | break :result_value result; |
| 7037 | } else { |
| 7038 | assert(result_ty.zigTypeTag(zcu) == .vector); |
| 7039 | const splat: std.wasm.SimdOpcode, const replace_lane: std.wasm.SimdOpcode = switch (elem_ty.toIntern()) { |
| 7040 | .u8_type, .i8_type => .{ .i8x16_splat, .i8x16_replace_lane }, |
| 7041 | .u16_type, .i16_type => .{ .i16x8_splat, .i16x8_replace_lane }, |
| 7042 | .u32_type, .i32_type => .{ .i32x4_splat, .i32x4_replace_lane }, |
| 7043 | .u64_type, .i64_type => .{ .i64x2_splat, .i64x2_replace_lane }, |
| 7044 | .f32_type => .{ .f32x4_splat, .f32x4_replace_lane }, |
| 7045 | .f64_type => .{ .f64x2_splat, .f64x2_replace_lane }, |
| 7046 | else => unreachable, |
| 7047 | }; |
| 7048 | |
| 7049 | // TODO: find best element to splat, if it is constant dont use splat at all |
| 7050 | try cg.emitWValue(try cg.resolveInst(elements[0])); |
| 7051 | const splat_extra: u32 = @intCast(cg.mir_extra.items.len); |
| 7052 | try cg.mir_extra.append(cg.gpa, @backingInt(splat)); |
| 7053 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = splat_extra } }); |
| 7054 | for (elements[1..], 1..) |elem, lane| { |
| 7055 | if (elem == elements[0]) continue; |
| 7056 | try cg.emitWValue(try cg.resolveInst(elem)); |
| 7057 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 7058 | try cg.mir_extra.appendSlice(cg.gpa, &.{ @backingInt(replace_lane), @intCast(lane) }); |
| 7059 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 7060 | } |
| 7061 | break :result_value .stack; |
| 7008 | 7062 | } |
| 7009 | | break :result_value result; |
| 7010 | 7063 | }, |
| 7011 | 7064 | .@"struct" => switch (result_ty.containerLayout(zcu)) { |
| 7012 | 7065 | .@"packed" => unreachable, // legalize .expand_packed_aggregate_init |
| ... | ... | @@ -7633,7 +7686,7 @@ fn airCmpxchg(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7633 | 7686 | 1 => .i32_atomic_rmw8_cmpxchg_u, |
| 7634 | 7687 | 2 => .i32_atomic_rmw16_cmpxchg_u, |
| 7635 | 7688 | 4 => .i32_atomic_rmw_cmpxchg, |
| 7636 | | 8 => .i32_atomic_rmw_cmpxchg, |
| 7689 | 8 => .i64_atomic_rmw_cmpxchg, |
| 7637 | 7690 | else => |size| return cg.fail("TODO: implement `@cmpxchg` for types with abi size '{d}'", .{size}), |
| 7638 | 7691 | }, .{ |
| 7639 | 7692 | .offset = ptr_operand.memOffset(), |
| ... | ... | @@ -7721,6 +7774,9 @@ fn airAtomicRmw(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 7721 | 7774 | const op: std.lang.AtomicRmwOp = extra.op(); |
| 7722 | 7775 | |
| 7723 | 7776 | if (cg.useAtomicFeature()) { |
| 7777 | if (ty.isAnyFloat()) { |
| 7778 | return cg.fail("TODO: atomic rmw on floats", .{}); |
| 7779 | } |
| 7724 | 7780 | const int_ty: IntType = .fromType(cg, ty); |
| 7725 | 7781 | switch (op) { |
| 7726 | 7782 | .Max, |