authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-08-03 11:14:10+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2025-08-03 12:10:23+03:30
log58b9200106c0eb721a13aea13e4ce55c4c0e340b
tree2655ca20b6cd6cee165700e37cff50b29c679072
parentd15a7b1b219b7f0b9ab9b870fb051005e3890a37
signaturelock-open Commit is signed but in an unrecognized format.

spirv: use packed struct's backing int type for shift value


2 files changed, 207 insertions(+), 328 deletions(-)

src/arch/spirv/CodeGen.zig+202-326
...@@ -431,15 +431,12 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {...@@ -431,15 +431,12 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {
431 const zcu = cg.module.zcu;431 const zcu = cg.module.zcu;
432 const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));432 const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
433 const ty_id = try cg.resolveType(ty, .indirect);433 const ty_id = try cg.resolveType(ty, .indirect);
434 const decl_ptr_ty_id = try cg.module.ptrType(ty_id, cg.module.storageClass(.generic));
435434
436 const spv_decl_index = blk: {435 const spv_decl_index = blk: {
437 const entry = try cg.module.uav_link.getOrPut(cg.module.gpa, .{ val, .function });436 const entry = try cg.module.uav_link.getOrPut(cg.module.gpa, .{ val, .function });
438 if (entry.found_existing) {437 if (entry.found_existing) {
439 try cg.addFunctionDep(entry.value_ptr.*, .function);438 try cg.addFunctionDep(entry.value_ptr.*, .function);
440439 return cg.module.declPtr(entry.value_ptr.*).result_id;
441 const result_id = cg.module.declPtr(entry.value_ptr.*).result_id;
442 return try cg.castToGeneric(decl_ptr_ty_id, result_id);
443 }440 }
444441
445 const spv_decl_index = try cg.module.allocDecl(.invocation_global);442 const spv_decl_index = try cg.module.allocDecl(.invocation_global);
...@@ -520,7 +517,7 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {...@@ -520,7 +517,7 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {
520 });517 });
521 }518 }
522519
523 return try cg.castToGeneric(decl_ptr_ty_id, result_id);520 return result_id;
524}521}
525522
526fn addFunctionDep(cg: *CodeGen, decl_index: Module.Decl.Index, storage_class: StorageClass) !void {523fn addFunctionDep(cg: *CodeGen, decl_index: Module.Decl.Index, storage_class: StorageClass) !void {
...@@ -535,21 +532,6 @@ fn addFunctionDep(cg: *CodeGen, decl_index: Module.Decl.Index, storage_class: St...@@ -535,21 +532,6 @@ fn addFunctionDep(cg: *CodeGen, decl_index: Module.Decl.Index, storage_class: St
535 }532 }
536}533}
537534
538fn castToGeneric(cg: *CodeGen, type_id: Id, ptr_id: Id) !Id {
539 const target = cg.module.zcu.getTarget();
540 if (target.cpu.has(.spirv, .generic_pointer)) {
541 const result_id = cg.module.allocId();
542 try cg.body.emit(cg.module.gpa, .OpPtrCastToGeneric, .{
543 .id_result_type = type_id,
544 .id_result = result_id,
545 .pointer = ptr_id,
546 });
547 return result_id;
548 }
549
550 return ptr_id;
551}
552
553/// Start a new SPIR-V block, Emits the label of the new block, and stores which535/// Start a new SPIR-V block, Emits the label of the new block, and stores which
554/// block we are currently generating.536/// block we are currently generating.
555/// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to537/// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to
...@@ -1209,11 +1191,7 @@ fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id {...@@ -1209,11 +1191,7 @@ fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id {
12091191
1210 const spv_decl_index = try cg.module.resolveNav(ip, nav_index);1192 const spv_decl_index = try cg.module.resolveNav(ip, nav_index);
1211 const spv_decl = cg.module.declPtr(spv_decl_index);1193 const spv_decl = cg.module.declPtr(spv_decl_index);
12121194 assert(spv_decl.kind != .func);
1213 const decl_id = switch (spv_decl.kind) {
1214 .func => unreachable, // TODO: Is this possible?
1215 .global, .invocation_global => spv_decl.result_id,
1216 };
12171195
1218 const storage_class = cg.module.storageClass(nav.getAddrspace());1196 const storage_class = cg.module.storageClass(nav.getAddrspace());
1219 try cg.addFunctionDep(spv_decl_index, storage_class);1197 try cg.addFunctionDep(spv_decl_index, storage_class);
...@@ -1221,23 +1199,18 @@ fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id {...@@ -1221,23 +1199,18 @@ fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id {
1221 const nav_ty_id = try cg.resolveType(nav_ty, .indirect);1199 const nav_ty_id = try cg.resolveType(nav_ty, .indirect);
1222 const decl_ptr_ty_id = try cg.module.ptrType(nav_ty_id, storage_class);1200 const decl_ptr_ty_id = try cg.module.ptrType(nav_ty_id, storage_class);
12231201
1224 const ptr_id = switch (storage_class) {
1225 .generic => try cg.castToGeneric(decl_ptr_ty_id, decl_id),
1226 else => decl_id,
1227 };
1228
1229 if (decl_ptr_ty_id != ty_id) {1202 if (decl_ptr_ty_id != ty_id) {
1230 // Differing pointer types, insert a cast.1203 // Differing pointer types, insert a cast.
1231 const casted_ptr_id = cg.module.allocId();1204 const casted_ptr_id = cg.module.allocId();
1232 try cg.body.emit(cg.module.gpa, .OpBitcast, .{1205 try cg.body.emit(cg.module.gpa, .OpBitcast, .{
1233 .id_result_type = ty_id,1206 .id_result_type = ty_id,
1234 .id_result = casted_ptr_id,1207 .id_result = casted_ptr_id,
1235 .operand = ptr_id,1208 .operand = spv_decl.result_id,
1236 });1209 });
1237 return casted_ptr_id;1210 return casted_ptr_id;
1238 } else {
1239 return ptr_id;
1240 }1211 }
1212
1213 return spv_decl.result_id;
1241}1214}
12421215
1243// Turn a Zig type's name into a cache reference.1216// Turn a Zig type's name into a cache reference.
...@@ -2120,28 +2093,7 @@ fn buildSelect(cg: *CodeGen, condition: Temporary, lhs: Temporary, rhs: Temporar...@@ -2120,28 +2093,7 @@ fn buildSelect(cg: *CodeGen, condition: Temporary, lhs: Temporary, rhs: Temporar
2120 return v.finalize(result_ty, results);2093 return v.finalize(result_ty, results);
2121}2094}
21222095
2123const CmpPredicate = enum {2096fn buildCmp(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary {
2124 l_eq,
2125 l_ne,
2126 i_ne,
2127 i_eq,
2128 s_lt,
2129 s_gt,
2130 s_le,
2131 s_ge,
2132 u_lt,
2133 u_gt,
2134 u_le,
2135 u_ge,
2136 f_oeq,
2137 f_une,
2138 f_olt,
2139 f_ole,
2140 f_ogt,
2141 f_oge,
2142};
2143
2144fn buildCmp(cg: *CodeGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary {
2145 const v = cg.vectorization(.{ lhs, rhs });2097 const v = cg.vectorization(.{ lhs, rhs });
2146 const ops = v.components();2098 const ops = v.components();
2147 const results = cg.module.allocIds(ops);2099 const results = cg.module.allocIds(ops);
...@@ -2153,27 +2105,6 @@ fn buildCmp(cg: *CodeGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !T...@@ -2153,27 +2105,6 @@ fn buildCmp(cg: *CodeGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !T
2153 const op_lhs = try v.prepare(cg, lhs);2105 const op_lhs = try v.prepare(cg, lhs);
2154 const op_rhs = try v.prepare(cg, rhs);2106 const op_rhs = try v.prepare(cg, rhs);
21552107
2156 const opcode: Opcode = switch (pred) {
2157 .l_eq => .OpLogicalEqual,
2158 .l_ne => .OpLogicalNotEqual,
2159 .i_eq => .OpIEqual,
2160 .i_ne => .OpINotEqual,
2161 .s_lt => .OpSLessThan,
2162 .s_gt => .OpSGreaterThan,
2163 .s_le => .OpSLessThanEqual,
2164 .s_ge => .OpSGreaterThanEqual,
2165 .u_lt => .OpULessThan,
2166 .u_gt => .OpUGreaterThan,
2167 .u_le => .OpULessThanEqual,
2168 .u_ge => .OpUGreaterThanEqual,
2169 .f_oeq => .OpFOrdEqual,
2170 .f_une => .OpFUnordNotEqual,
2171 .f_olt => .OpFOrdLessThan,
2172 .f_ole => .OpFOrdLessThanEqual,
2173 .f_ogt => .OpFOrdGreaterThan,
2174 .f_oge => .OpFOrdGreaterThanEqual,
2175 };
2176
2177 for (0..ops) |i| {2108 for (0..ops) |i| {
2178 try cg.body.emitRaw(cg.module.gpa, opcode, 4);2109 try cg.body.emitRaw(cg.module.gpa, opcode, 4);
2179 cg.body.writeOperand(Id, op_result_ty_id);2110 cg.body.writeOperand(Id, op_result_ty_id);
...@@ -2278,7 +2209,10 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary {...@@ -2278,7 +2209,10 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary {
2278 .log,2209 .log,
2279 .log2,2210 .log2,
2280 .log10,2211 .log10,
2281 => return cg.todo("implement unary operation '{s}' for {s} os", .{ @tagName(op), @tagName(target.os.tag) }),2212 => return cg.todo(
2213 "implement unary operation '{s}' for {s} os",
2214 .{ @tagName(op), @tagName(target.os.tag) },
2215 ),
2282 else => unreachable,2216 else => unreachable,
2283 },2217 },
2284 else => unreachable,2218 else => unreachable,
...@@ -2298,40 +2232,8 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary {...@@ -2298,40 +2232,8 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary {
2298 return v.finalize(result_ty, results);2232 return v.finalize(result_ty, results);
2299}2233}
23002234
2301const BinaryOp = enum {2235fn buildBinary(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary {
2302 i_add,
2303 f_add,
2304 i_sub,
2305 f_sub,
2306 i_mul,
2307 f_mul,
2308 s_div,
2309 u_div,
2310 f_div,
2311 s_rem,
2312 f_rem,
2313 s_mod,
2314 u_mod,
2315 f_mod,
2316 srl,
2317 sra,
2318 sll,
2319 bit_and,
2320 bit_or,
2321 bit_xor,
2322 f_max,
2323 s_max,
2324 u_max,
2325 f_min,
2326 s_min,
2327 u_min,
2328 l_and,
2329 l_or,
2330};
2331
2332fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {
2333 const zcu = cg.module.zcu;2236 const zcu = cg.module.zcu;
2334 const target = cg.module.zcu.getTarget();
23352237
2336 const v = cg.vectorization(.{ lhs, rhs });2238 const v = cg.vectorization(.{ lhs, rhs });
2337 const ops = v.components();2239 const ops = v.components();
...@@ -2344,73 +2246,12 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp...@@ -2344,73 +2246,12 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp
2344 const op_lhs = try v.prepare(cg, lhs);2246 const op_lhs = try v.prepare(cg, lhs);
2345 const op_rhs = try v.prepare(cg, rhs);2247 const op_rhs = try v.prepare(cg, rhs);
23462248
2347 if (switch (op) {2249 for (0..ops) |i| {
2348 .i_add => .OpIAdd,2250 try cg.body.emitRaw(cg.module.gpa, opcode, 4);
2349 .f_add => .OpFAdd,2251 cg.body.writeOperand(Id, op_result_ty_id);
2350 .i_sub => .OpISub,2252 cg.body.writeOperand(Id, results.at(i));
2351 .f_sub => .OpFSub,2253 cg.body.writeOperand(Id, op_lhs.at(i));
2352 .i_mul => .OpIMul,2254 cg.body.writeOperand(Id, op_rhs.at(i));
2353 .f_mul => .OpFMul,
2354 .s_div => .OpSDiv,
2355 .u_div => .OpUDiv,
2356 .f_div => .OpFDiv,
2357 .s_rem => .OpSRem,
2358 .f_rem => .OpFRem,
2359 .s_mod => .OpSMod,
2360 .u_mod => .OpUMod,
2361 .f_mod => .OpFMod,
2362 .srl => .OpShiftRightLogical,
2363 .sra => .OpShiftRightArithmetic,
2364 .sll => .OpShiftLeftLogical,
2365 .bit_and => .OpBitwiseAnd,
2366 .bit_or => .OpBitwiseOr,
2367 .bit_xor => .OpBitwiseXor,
2368 .l_and => .OpLogicalAnd,
2369 .l_or => .OpLogicalOr,
2370 else => @as(?Opcode, null),
2371 }) |opcode| {
2372 for (0..ops) |i| {
2373 try cg.body.emitRaw(cg.module.gpa, opcode, 4);
2374 cg.body.writeOperand(Id, op_result_ty_id);
2375 cg.body.writeOperand(Id, results.at(i));
2376 cg.body.writeOperand(Id, op_lhs.at(i));
2377 cg.body.writeOperand(Id, op_rhs.at(i));
2378 }
2379 } else {
2380 const set = try cg.importExtendedSet();
2381
2382 // TODO: Put these numbers in some definition
2383 const extinst: u32 = switch (target.os.tag) {
2384 .opencl => switch (op) {
2385 .f_max => 27, // fmax
2386 .s_max => 156, // s_max
2387 .u_max => 157, // u_max
2388 .f_min => 28, // fmin
2389 .s_min => 158, // s_min
2390 .u_min => 159, // u_min
2391 else => unreachable,
2392 },
2393 .vulkan, .opengl => switch (op) {
2394 .f_max => 40, // FMax
2395 .s_max => 42, // SMax
2396 .u_max => 41, // UMax
2397 .f_min => 37, // FMin
2398 .s_min => 39, // SMin
2399 .u_min => 38, // UMin
2400 else => unreachable,
2401 },
2402 else => unreachable,
2403 };
2404
2405 for (0..ops) |i| {
2406 try cg.body.emit(cg.module.gpa, .OpExtInst, .{
2407 .id_result_type = op_result_ty_id,
2408 .id_result = results.at(i),
2409 .set = set,
2410 .instruction = .{ .inst = extinst },
2411 .id_ref_4 = &.{ op_lhs.at(i), op_rhs.at(i) },
2412 });
2413 }
2414 }2255 }
24152256
2416 return v.finalize(result_ty, results);2257 return v.finalize(result_ty, results);
...@@ -2420,10 +2261,7 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp...@@ -2420,10 +2261,7 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp
2420/// or OpIMul and s_mul_hi or u_mul_hi on OpenCL.2261/// or OpIMul and s_mul_hi or u_mul_hi on OpenCL.
2421fn buildWideMul(2262fn buildWideMul(
2422 cg: *CodeGen,2263 cg: *CodeGen,
2423 op: enum {2264 signedness: std.builtin.Signedness,
2424 s_mul_extended,
2425 u_mul_extended,
2426 },
2427 lhs: Temporary,2265 lhs: Temporary,
2428 rhs: Temporary,2266 rhs: Temporary,
2429) !struct { Temporary, Temporary } {2267) !struct { Temporary, Temporary } {
...@@ -2450,9 +2288,9 @@ fn buildWideMul(...@@ -2450,9 +2288,9 @@ fn buildWideMul(
2450 // OpUMulExtended. For these we will use the OpenCL s_mul_hi to compute the high-order bits2288 // OpUMulExtended. For these we will use the OpenCL s_mul_hi to compute the high-order bits
2451 // instead.2289 // instead.
2452 const set = try cg.importExtendedSet();2290 const set = try cg.importExtendedSet();
2453 const overflow_inst: u32 = switch (op) {2291 const overflow_inst: u32 = switch (signedness) {
2454 .s_mul_extended => 160, // s_mul_hi2292 .signed => 160, // s_mul_hi
2455 .u_mul_extended => 203, // u_mul_hi2293 .unsigned => 203, // u_mul_hi
2456 };2294 };
24572295
2458 for (0..ops) |i| {2296 for (0..ops) |i| {
...@@ -2481,9 +2319,9 @@ fn buildWideMul(...@@ -2481,9 +2319,9 @@ fn buildWideMul(
2481 }));2319 }));
2482 const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);2320 const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
24832321
2484 const opcode: Opcode = switch (op) {2322 const opcode: Opcode = switch (signedness) {
2485 .s_mul_extended => .OpSMulExtended,2323 .signed => .OpSMulExtended,
2486 .u_mul_extended => .OpUMulExtended,2324 .unsigned => .OpUMulExtended,
2487 };2325 };
24882326
2489 for (0..ops) |i| {2327 for (0..ops) |i| {
...@@ -2718,7 +2556,7 @@ fn convertToDirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id {...@@ -2718,7 +2556,7 @@ fn convertToDirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id {
2718 };2556 };
27192557
2720 const result = try cg.buildCmp(2558 const result = try cg.buildCmp(
2721 .i_ne,2559 .OpINotEqual,
2722 Temporary.init(operand_ty, operand_id),2560 Temporary.init(operand_ty, operand_id),
2723 Temporary.init(.u1, false_id),2561 Temporary.init(.u1, false_id),
2724 );2562 );
...@@ -2817,9 +2655,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {...@@ -2817,9 +2655,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
2817 const air_tags = cg.air.instructions.items(.tag);2655 const air_tags = cg.air.instructions.items(.tag);
2818 const maybe_result_id: ?Id = switch (air_tags[@intFromEnum(inst)]) {2656 const maybe_result_id: ?Id = switch (air_tags[@intFromEnum(inst)]) {
2819 // zig fmt: off2657 // zig fmt: off
2820 .add, .add_wrap, .add_optimized => try cg.airArithOp(inst, .f_add, .i_add, .i_add),2658 .add, .add_wrap, .add_optimized => try cg.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
2821 .sub, .sub_wrap, .sub_optimized => try cg.airArithOp(inst, .f_sub, .i_sub, .i_sub),2659 .sub, .sub_wrap, .sub_optimized => try cg.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
2822 .mul, .mul_wrap, .mul_optimized => try cg.airArithOp(inst, .f_mul, .i_mul, .i_mul),2660 .mul, .mul_wrap, .mul_optimized => try cg.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
28232661
2824 .sqrt => try cg.airUnOpSimple(inst, .sqrt),2662 .sqrt => try cg.airUnOpSimple(inst, .sqrt),
2825 .sin => try cg.airUnOpSimple(inst, .sin),2663 .sin => try cg.airUnOpSimple(inst, .sin),
...@@ -2837,15 +2675,15 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {...@@ -2837,15 +2675,15 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
2837 .trunc_float => try cg.airUnOpSimple(inst, .trunc),2675 .trunc_float => try cg.airUnOpSimple(inst, .trunc),
2838 .neg, .neg_optimized => try cg.airUnOpSimple(inst, .f_neg),2676 .neg, .neg_optimized => try cg.airUnOpSimple(inst, .f_neg),
28392677
2840 .div_float, .div_float_optimized => try cg.airArithOp(inst, .f_div, .s_div, .u_div),2678 .div_float, .div_float_optimized => try cg.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv),
2841 .div_floor, .div_floor_optimized => try cg.airDivFloor(inst),2679 .div_floor, .div_floor_optimized => try cg.airDivFloor(inst),
2842 .div_trunc, .div_trunc_optimized => try cg.airDivTrunc(inst),2680 .div_trunc, .div_trunc_optimized => try cg.airDivTrunc(inst),
28432681
2844 .rem, .rem_optimized => try cg.airArithOp(inst, .f_rem, .s_rem, .u_mod),2682 .rem, .rem_optimized => try cg.airArithOp(inst, .OpFRem, .OpSRem, .OpUMod),
2845 .mod, .mod_optimized => try cg.airArithOp(inst, .f_mod, .s_mod, .u_mod),2683 .mod, .mod_optimized => try cg.airArithOp(inst, .OpFMod, .OpSMod, .OpUMod),
28462684
2847 .add_with_overflow => try cg.airAddSubOverflow(inst, .i_add, .u_lt, .s_lt),2685 .add_with_overflow => try cg.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),
2848 .sub_with_overflow => try cg.airAddSubOverflow(inst, .i_sub, .u_gt, .s_gt),2686 .sub_with_overflow => try cg.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
2849 .mul_with_overflow => try cg.airMulOverflow(inst),2687 .mul_with_overflow => try cg.airMulOverflow(inst),
2850 .shl_with_overflow => try cg.airShlOverflow(inst),2688 .shl_with_overflow => try cg.airShlOverflow(inst),
28512689
...@@ -2864,14 +2702,14 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {...@@ -2864,14 +2702,14 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
2864 .ptr_add => try cg.airPtrAdd(inst),2702 .ptr_add => try cg.airPtrAdd(inst),
2865 .ptr_sub => try cg.airPtrSub(inst),2703 .ptr_sub => try cg.airPtrSub(inst),
28662704
2867 .bit_and => try cg.airBinOpSimple(inst, .bit_and),2705 .bit_and => try cg.airBinOpSimple(inst, .OpBitwiseAnd),
2868 .bit_or => try cg.airBinOpSimple(inst, .bit_or),2706 .bit_or => try cg.airBinOpSimple(inst, .OpBitwiseOr),
2869 .xor => try cg.airBinOpSimple(inst, .bit_xor),2707 .xor => try cg.airBinOpSimple(inst, .OpBitwiseXor),
2870 .bool_and => try cg.airBinOpSimple(inst, .l_and),2708 .bool_and => try cg.airBinOpSimple(inst, .OpLogicalAnd),
2871 .bool_or => try cg.airBinOpSimple(inst, .l_or),2709 .bool_or => try cg.airBinOpSimple(inst, .OpLogicalOr),
28722710
2873 .shl, .shl_exact => try cg.airShift(inst, .sll, .sll),2711 .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical),
2874 .shr, .shr_exact => try cg.airShift(inst, .srl, .sra),2712 .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic),
28752713
2876 .min => try cg.airMinMax(inst, .min),2714 .min => try cg.airMinMax(inst, .min),
2877 .max => try cg.airMinMax(inst, .max),2715 .max => try cg.airMinMax(inst, .max),
...@@ -2983,7 +2821,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {...@@ -2983,7 +2821,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
2983 try cg.inst_results.putNoClobber(gpa, inst, result_id);2821 try cg.inst_results.putNoClobber(gpa, inst, result_id);
2984}2822}
29852823
2986fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: BinaryOp) !?Id {2824fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id {
2987 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2825 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2988 const lhs = try cg.temporary(bin_op.lhs);2826 const lhs = try cg.temporary(bin_op.lhs);
2989 const rhs = try cg.temporary(bin_op.rhs);2827 const rhs = try cg.temporary(bin_op.rhs);
...@@ -2992,7 +2830,7 @@ fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: BinaryOp) !?Id {...@@ -2992,7 +2830,7 @@ fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: BinaryOp) !?Id {
2992 return try result.materialize(cg);2830 return try result.materialize(cg);
2993}2831}
29942832
2995fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: BinaryOp, signed: BinaryOp) !?Id {2833fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id {
2996 const zcu = cg.module.zcu;2834 const zcu = cg.module.zcu;
2997 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2835 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
29982836
...@@ -3042,28 +2880,77 @@ fn airMinMax(cg: *CodeGen, inst: Air.Inst.Index, op: MinMax) !?Id {...@@ -3042,28 +2880,77 @@ fn airMinMax(cg: *CodeGen, inst: Air.Inst.Index, op: MinMax) !?Id {
3042}2880}
30432881
3044fn minMax(cg: *CodeGen, lhs: Temporary, rhs: Temporary, op: MinMax) !Temporary {2882fn minMax(cg: *CodeGen, lhs: Temporary, rhs: Temporary, op: MinMax) !Temporary {
2883 const zcu = cg.module.zcu;
2884 const target = zcu.getTarget();
3045 const info = cg.arithmeticTypeInfo(lhs.ty);2885 const info = cg.arithmeticTypeInfo(lhs.ty);
30462886
3047 const binop: BinaryOp = switch (info.class) {2887 const v = cg.vectorization(.{ lhs, rhs });
3048 .float => switch (op) {2888 const ops = v.components();
3049 .min => .f_min,2889 const results = cg.module.allocIds(ops);
3050 .max => .f_max,2890
2891 const op_result_ty = lhs.ty.scalarType(zcu);
2892 const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
2893 const result_ty = try v.resultType(cg, lhs.ty);
2894
2895 const op_lhs = try v.prepare(cg, lhs);
2896 const op_rhs = try v.prepare(cg, rhs);
2897
2898 const ext_inst: u32 = switch (target.os.tag) {
2899 .opencl => switch (info.class) {
2900 .float => switch (op) {
2901 .min => 28, // fmin
2902 .max => 27, // fmax
2903 },
2904 .integer,
2905 .strange_integer,
2906 .composite_integer,
2907 => switch (info.signedness) {
2908 .signed => switch (op) {
2909 .min => 158, // s_min
2910 .max => 156, // s_max
2911 },
2912 .unsigned => switch (op) {
2913 .min => 159, // u_min
2914 .max => 157, // u_max
2915 },
2916 },
2917 .bool => unreachable,
3051 },2918 },
3052 .integer, .strange_integer => switch (info.signedness) {2919 .vulkan, .opengl => switch (info.class) {
3053 .signed => switch (op) {2920 .float => switch (op) {
3054 .min => .s_min,2921 .min => 37, // FMin
3055 .max => .s_max,2922 .max => 40, // FMax
3056 },2923 },
3057 .unsigned => switch (op) {2924 .integer,
3058 .min => .u_min,2925 .strange_integer,
3059 .max => .u_max,2926 .composite_integer,
2927 => switch (info.signedness) {
2928 .signed => switch (op) {
2929 .min => 39, // SMin
2930 .max => 42, // SMax
2931 },
2932 .unsigned => switch (op) {
2933 .min => 38, // UMin
2934 .max => 41, // UMax
2935 },
3060 },2936 },
2937 .bool => unreachable,
3061 },2938 },
3062 .composite_integer => unreachable, // TODO2939 else => unreachable,
3063 .bool => unreachable,
3064 };2940 };
30652941
3066 return try cg.buildBinary(binop, lhs, rhs);2942 const set = try cg.importExtendedSet();
2943 for (0..ops) |i| {
2944 try cg.body.emit(cg.module.gpa, .OpExtInst, .{
2945 .id_result_type = op_result_ty_id,
2946 .id_result = results.at(i),
2947 .set = set,
2948 .instruction = .{ .inst = ext_inst },
2949 .id_ref_4 = &.{ op_lhs.at(i), op_rhs.at(i) },
2950 });
2951 }
2952
2953 return v.finalize(result_ty, results);
3067}2954}
30682955
3069/// This function normalizes values to a canonical representation2956/// This function normalizes values to a canonical representation
...@@ -3083,14 +2970,14 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar...@@ -3083,14 +2970,14 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar
3083 .unsigned => {2970 .unsigned => {
3084 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;2971 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
3085 const mask_id = try cg.constInt(ty.scalarType(zcu), mask_value);2972 const mask_id = try cg.constInt(ty.scalarType(zcu), mask_value);
3086 return try cg.buildBinary(.bit_and, value, Temporary.init(ty.scalarType(zcu), mask_id));2973 return try cg.buildBinary(.OpBitwiseAnd, value, Temporary.init(ty.scalarType(zcu), mask_id));
3087 },2974 },
3088 .signed => {2975 .signed => {
3089 // Shift left and right so that we can copy the sight bit that way.2976 // Shift left and right so that we can copy the sight bit that way.
3090 const shift_amt_id = try cg.constInt(ty.scalarType(zcu), info.backing_bits - info.bits);2977 const shift_amt_id = try cg.constInt(ty.scalarType(zcu), info.backing_bits - info.bits);
3091 const shift_amt: Temporary = .init(ty.scalarType(zcu), shift_amt_id);2978 const shift_amt: Temporary = .init(ty.scalarType(zcu), shift_amt_id);
3092 const left = try cg.buildBinary(.sll, value, shift_amt);2979 const left = try cg.buildBinary(.OpShiftLeftLogical, value, shift_amt);
3093 return try cg.buildBinary(.sra, left, shift_amt);2980 return try cg.buildBinary(.OpShiftRightArithmetic, left, shift_amt);
3094 },2981 },
3095 },2982 },
3096 }2983 }
...@@ -3108,7 +2995,7 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3108,7 +2995,7 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3108 .integer, .strange_integer => {2995 .integer, .strange_integer => {
3109 switch (info.signedness) {2996 switch (info.signedness) {
3110 .unsigned => {2997 .unsigned => {
3111 const result = try cg.buildBinary(.u_div, lhs, rhs);2998 const result = try cg.buildBinary(.OpUDiv, lhs, rhs);
3112 return try result.materialize(cg);2999 return try result.materialize(cg);
3113 },3000 },
3114 .signed => {},3001 .signed => {},
...@@ -3118,26 +3005,26 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3118,26 +3005,26 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3118 // (a / b) - (a % b != 0 && a < 0 != b < 0);3005 // (a / b) - (a % b != 0 && a < 0 != b < 0);
3119 // There shouldn't be any overflow issues.3006 // There shouldn't be any overflow issues.
31203007
3121 const div = try cg.buildBinary(.s_div, lhs, rhs);3008 const div = try cg.buildBinary(.OpSDiv, lhs, rhs);
3122 const rem = try cg.buildBinary(.s_rem, lhs, rhs);3009 const rem = try cg.buildBinary(.OpSRem, lhs, rhs);
31233010
3124 const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));3011 const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
31253012
3126 const rem_is_not_zero = try cg.buildCmp(.i_ne, rem, zero);3013 const rem_is_not_zero = try cg.buildCmp(.OpINotEqual, rem, zero);
31273014
3128 const result_negative = try cg.buildCmp(3015 const result_negative = try cg.buildCmp(
3129 .l_ne,3016 .OpLogicalNotEqual,
3130 try cg.buildCmp(.s_lt, lhs, zero),3017 try cg.buildCmp(.OpSLessThan, lhs, zero),
3131 try cg.buildCmp(.s_lt, rhs, zero),3018 try cg.buildCmp(.OpSLessThan, rhs, zero),
3132 );3019 );
3133 const rem_is_not_zero_and_result_is_negative = try cg.buildBinary(3020 const rem_is_not_zero_and_result_is_negative = try cg.buildBinary(
3134 .l_and,3021 .OpLogicalAnd,
3135 rem_is_not_zero,3022 rem_is_not_zero,
3136 result_negative,3023 result_negative,
3137 );3024 );
31383025
3139 const result = try cg.buildBinary(3026 const result = try cg.buildBinary(
3140 .i_sub,3027 .OpISub,
3141 div,3028 div,
3142 try cg.intFromBool2(rem_is_not_zero_and_result_is_negative, div.ty),3029 try cg.intFromBool2(rem_is_not_zero_and_result_is_negative, div.ty),
3143 );3030 );
...@@ -3145,7 +3032,7 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3145,7 +3032,7 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3145 return try result.materialize(cg);3032 return try result.materialize(cg);
3146 },3033 },
3147 .float => {3034 .float => {
3148 const div = try cg.buildBinary(.f_div, lhs, rhs);3035 const div = try cg.buildBinary(.OpFDiv, lhs, rhs);
3149 const result = try cg.buildUnary(.floor, div);3036 const result = try cg.buildUnary(.floor, div);
3150 return try result.materialize(cg);3037 return try result.materialize(cg);
3151 },3038 },
...@@ -3164,16 +3051,16 @@ fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3164,16 +3051,16 @@ fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3164 .composite_integer => unreachable, // TODO3051 .composite_integer => unreachable, // TODO
3165 .integer, .strange_integer => switch (info.signedness) {3052 .integer, .strange_integer => switch (info.signedness) {
3166 .unsigned => {3053 .unsigned => {
3167 const result = try cg.buildBinary(.u_div, lhs, rhs);3054 const result = try cg.buildBinary(.OpUDiv, lhs, rhs);
3168 return try result.materialize(cg);3055 return try result.materialize(cg);
3169 },3056 },
3170 .signed => {3057 .signed => {
3171 const result = try cg.buildBinary(.s_div, lhs, rhs);3058 const result = try cg.buildBinary(.OpSDiv, lhs, rhs);
3172 return try result.materialize(cg);3059 return try result.materialize(cg);
3173 },3060 },
3174 },3061 },
3175 .float => {3062 .float => {
3176 const div = try cg.buildBinary(.f_div, lhs, rhs);3063 const div = try cg.buildBinary(.OpFDiv, lhs, rhs);
3177 const result = try cg.buildUnary(.trunc, div);3064 const result = try cg.buildUnary(.trunc, div);
3178 return try result.materialize(cg);3065 return try result.materialize(cg);
3179 },3066 },
...@@ -3191,9 +3078,9 @@ fn airUnOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: UnaryOp) !?Id {...@@ -3191,9 +3078,9 @@ fn airUnOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: UnaryOp) !?Id {
3191fn airArithOp(3078fn airArithOp(
3192 cg: *CodeGen,3079 cg: *CodeGen,
3193 inst: Air.Inst.Index,3080 inst: Air.Inst.Index,
3194 comptime fop: BinaryOp,3081 comptime fop: Opcode,
3195 comptime sop: BinaryOp,3082 comptime sop: Opcode,
3196 comptime uop: BinaryOp,3083 comptime uop: Opcode,
3197) !?Id {3084) !?Id {
3198 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3085 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
31993086
...@@ -3253,11 +3140,11 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary {...@@ -3253,11 +3140,11 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary {
3253fn airAddSubOverflow(3140fn airAddSubOverflow(
3254 cg: *CodeGen,3141 cg: *CodeGen,
3255 inst: Air.Inst.Index,3142 inst: Air.Inst.Index,
3256 comptime add: BinaryOp,3143 comptime add: Opcode,
3257 comptime ucmp: CmpPredicate,3144 u_opcode: Opcode,
3258 comptime scmp: CmpPredicate,3145 s_opcode: Opcode,
3259) !?Id {3146) !?Id {
3260 _ = scmp;3147 _ = s_opcode;
3261 // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers,3148 // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers,
3262 // there is in both cases only one extra operation required. For signed operations,3149 // there is in both cases only one extra operation required. For signed operations,
3263 // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually3150 // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually
...@@ -3285,7 +3172,7 @@ fn airAddSubOverflow(...@@ -3285,7 +3172,7 @@ fn airAddSubOverflow(
3285 const overflowed = switch (info.signedness) {3172 const overflowed = switch (info.signedness) {
3286 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.3173 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
3287 // For subtraction the conditions need to be swapped.3174 // For subtraction the conditions need to be swapped.
3288 .unsigned => try cg.buildCmp(ucmp, result, lhs),3175 .unsigned => try cg.buildCmp(u_opcode, result, lhs),
3289 // For signed operations, we check the signs of the operands and the result.3176 // For signed operations, we check the signs of the operands and the result.
3290 .signed => blk: {3177 .signed => blk: {
3291 // Signed overflow detection using the sign bits of the operands and the result.3178 // Signed overflow detection using the sign bits of the operands and the result.
...@@ -3297,19 +3184,19 @@ fn airAddSubOverflow(...@@ -3297,19 +3184,19 @@ fn airAddSubOverflow(
3297 // (sign(a) != sign(b)) && (sign(a) != sign(result))3184 // (sign(a) != sign(b)) && (sign(a) != sign(result))
3298 const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0));3185 const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0));
32993186
3300 const lhs_is_neg = try cg.buildCmp(.s_lt, lhs, zero);3187 const lhs_is_neg = try cg.buildCmp(.OpSLessThan, lhs, zero);
3301 const rhs_is_neg = try cg.buildCmp(.s_lt, rhs, zero);3188 const rhs_is_neg = try cg.buildCmp(.OpSLessThan, rhs, zero);
3302 const result_is_neg = try cg.buildCmp(.s_lt, result, zero);3189 const result_is_neg = try cg.buildCmp(.OpSLessThan, result, zero);
33033190
3304 const signs_match = try cg.buildCmp(.l_eq, lhs_is_neg, rhs_is_neg);3191 const signs_match = try cg.buildCmp(.OpLogicalEqual, lhs_is_neg, rhs_is_neg);
3305 const result_sign_differs = try cg.buildCmp(.l_ne, lhs_is_neg, result_is_neg);3192 const result_sign_differs = try cg.buildCmp(.OpLogicalNotEqual, lhs_is_neg, result_is_neg);
33063193
3307 const overflow_condition = if (add == .i_add)3194 const overflow_condition = if (add == .OpIAdd)
3308 signs_match3195 signs_match
3309 else // .i_sub3196 else // .OpISub
3310 try cg.buildUnary(.l_not, signs_match);3197 try cg.buildUnary(.l_not, signs_match);
33113198
3312 break :blk try cg.buildBinary(.l_and, overflow_condition, result_sign_differs);3199 break :blk try cg.buildCmp(.OpLogicalAnd, overflow_condition, result_sign_differs);
3313 },3200 },
3314 };3201 };
33153202
...@@ -3361,23 +3248,23 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3361,23 +3248,23 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3361 const casted_lhs = try cg.buildConvert(op_ty, lhs);3248 const casted_lhs = try cg.buildConvert(op_ty, lhs);
3362 const casted_rhs = try cg.buildConvert(op_ty, rhs);3249 const casted_rhs = try cg.buildConvert(op_ty, rhs);
33633250
3364 const full_result = try cg.buildBinary(.i_mul, casted_lhs, casted_rhs);3251 const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs);
33653252
3366 const low_bits = try cg.buildConvert(lhs.ty, full_result);3253 const low_bits = try cg.buildConvert(lhs.ty, full_result);
3367 const result = try cg.normalize(low_bits, info);3254 const result = try cg.normalize(low_bits, info);
33683255
3369 // Shift the result bits away to get the overflow bits.3256 // Shift the result bits away to get the overflow bits.
3370 const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits));3257 const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits));
3371 const overflow = try cg.buildBinary(.srl, full_result, shift);3258 const overflow = try cg.buildBinary(.OpShiftRightLogical, full_result, shift);
33723259
3373 // Directly check if its zero in the op_ty without converting first.3260 // Directly check if its zero in the op_ty without converting first.
3374 const zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));3261 const zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));
3375 const overflowed = try cg.buildCmp(.i_ne, zero, overflow);3262 const overflowed = try cg.buildCmp(.OpINotEqual, zero, overflow);
33763263
3377 break :blk .{ result, overflowed };3264 break :blk .{ result, overflowed };
3378 }3265 }
33793266
3380 const low_bits, const high_bits = try cg.buildWideMul(.u_mul_extended, lhs, rhs);3267 const low_bits, const high_bits = try cg.buildWideMul(.unsigned, lhs, rhs);
33813268
3382 // Truncate the result, if required.3269 // Truncate the result, if required.
3383 const result = try cg.normalize(low_bits, info);3270 const result = try cg.normalize(low_bits, info);
...@@ -3386,17 +3273,17 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3386,17 +3273,17 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3386 // high bits of the low word of the result (those outside the range of the3273 // high bits of the low word of the result (those outside the range of the
3387 // int) are nonzero.3274 // int) are nonzero.
3388 const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));3275 const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
3389 const high_overflowed = try cg.buildCmp(.i_ne, zero, high_bits);3276 const high_overflowed = try cg.buildCmp(.OpINotEqual, zero, high_bits);
33903277
3391 // If no overflow bits in low_bits, no extra work needs to be done.3278 // If no overflow bits in low_bits, no extra work needs to be done.
3392 if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed };3279 if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed };
33933280
3394 // Shift the result bits away to get the overflow bits.3281 // Shift the result bits away to get the overflow bits.
3395 const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits));3282 const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits));
3396 const low_overflow = try cg.buildBinary(.srl, low_bits, shift);3283 const low_overflow = try cg.buildBinary(.OpShiftRightLogical, low_bits, shift);
3397 const low_overflowed = try cg.buildCmp(.i_ne, zero, low_overflow);3284 const low_overflowed = try cg.buildCmp(.OpINotEqual, zero, low_overflow);
33983285
3399 const overflowed = try cg.buildBinary(.l_or, low_overflowed, high_overflowed);3286 const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed);
34003287
3401 break :blk .{ result, overflowed };3288 break :blk .{ result, overflowed };
3402 },3289 },
...@@ -3412,16 +3299,16 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3412,16 +3299,16 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3412 // (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0)3299 // (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0)
34133300
3414 const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));3301 const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
3415 const lhs_negative = try cg.buildCmp(.s_lt, lhs, zero);3302 const lhs_negative = try cg.buildCmp(.OpSLessThan, lhs, zero);
3416 const rhs_negative = try cg.buildCmp(.s_lt, rhs, zero);3303 const rhs_negative = try cg.buildCmp(.OpSLessThan, rhs, zero);
3417 const lhs_positive = try cg.buildCmp(.s_gt, lhs, zero);3304 const lhs_positive = try cg.buildCmp(.OpSGreaterThan, lhs, zero);
3418 const rhs_positive = try cg.buildCmp(.s_gt, rhs, zero);3305 const rhs_positive = try cg.buildCmp(.OpSGreaterThan, rhs, zero);
34193306
3420 // Set to `true` if we expect -1.3307 // Set to `true` if we expect -1.
3421 const expected_overflow_bit = try cg.buildBinary(3308 const expected_overflow_bit = try cg.buildBinary(
3422 .l_or,3309 .OpLogicalOr,
3423 try cg.buildBinary(.l_and, lhs_positive, rhs_negative),3310 try cg.buildCmp(.OpLogicalAnd, lhs_positive, rhs_negative),
3424 try cg.buildBinary(.l_and, lhs_negative, rhs_positive),3311 try cg.buildCmp(.OpLogicalAnd, lhs_negative, rhs_positive),
3425 );3312 );
34263313
3427 if (maybe_op_ty_bits) |op_ty_bits| {3314 if (maybe_op_ty_bits) |op_ty_bits| {
...@@ -3430,7 +3317,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3430,7 +3317,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3430 const casted_lhs = try cg.buildConvert(op_ty, lhs);3317 const casted_lhs = try cg.buildConvert(op_ty, lhs);
3431 const casted_rhs = try cg.buildConvert(op_ty, rhs);3318 const casted_rhs = try cg.buildConvert(op_ty, rhs);
34323319
3433 const full_result = try cg.buildBinary(.i_mul, casted_lhs, casted_rhs);3320 const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs);
34343321
3435 // Truncate to the result type.3322 // Truncate to the result type.
3436 const low_bits = try cg.buildConvert(lhs.ty, full_result);3323 const low_bits = try cg.buildConvert(lhs.ty, full_result);
...@@ -3443,18 +3330,18 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3443,18 +3330,18 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3443 const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits - 1));3330 const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits - 1));
3444 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set3331 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
3445 // for negative cases.3332 // for negative cases.
3446 const overflow = try cg.buildBinary(.sra, full_result, shift);3333 const overflow = try cg.buildBinary(.OpShiftRightArithmetic, full_result, shift);
34473334
3448 const long_all_set: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, -1));3335 const long_all_set: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, -1));
3449 const long_zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));3336 const long_zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));
3450 const mask = try cg.buildSelect(expected_overflow_bit, long_all_set, long_zero);3337 const mask = try cg.buildSelect(expected_overflow_bit, long_all_set, long_zero);
34513338
3452 const overflowed = try cg.buildCmp(.i_ne, mask, overflow);3339 const overflowed = try cg.buildCmp(.OpINotEqual, mask, overflow);
34533340
3454 break :blk .{ result, overflowed };3341 break :blk .{ result, overflowed };
3455 }3342 }
34563343
3457 const low_bits, const high_bits = try cg.buildWideMul(.s_mul_extended, lhs, rhs);3344 const low_bits, const high_bits = try cg.buildWideMul(.signed, lhs, rhs);
34583345
3459 // Truncate result if required.3346 // Truncate result if required.
3460 const result = try cg.normalize(low_bits, info);3347 const result = try cg.normalize(low_bits, info);
...@@ -3465,7 +3352,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3465,7 +3352,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3465 // Like with unsigned, overflow happened if high_bits are not the ones we expect,3352 // Like with unsigned, overflow happened if high_bits are not the ones we expect,
3466 // and we also need to check some ones from the low bits.3353 // and we also need to check some ones from the low bits.
34673354
3468 const high_overflowed = try cg.buildCmp(.i_ne, mask, high_bits);3355 const high_overflowed = try cg.buildCmp(.OpINotEqual, mask, high_bits);
34693356
3470 // If no overflow bits in low_bits, no extra work needs to be done.3357 // If no overflow bits in low_bits, no extra work needs to be done.
3471 // Careful, we still have to check the sign bit, so this branch3358 // Careful, we still have to check the sign bit, so this branch
...@@ -3476,10 +3363,10 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3476,10 +3363,10 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3476 const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits - 1));3363 const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits - 1));
3477 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set3364 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
3478 // for negative cases.3365 // for negative cases.
3479 const low_overflow = try cg.buildBinary(.sra, low_bits, shift);3366 const low_overflow = try cg.buildBinary(.OpShiftRightArithmetic, low_bits, shift);
3480 const low_overflowed = try cg.buildCmp(.i_ne, mask, low_overflow);3367 const low_overflowed = try cg.buildCmp(.OpINotEqual, mask, low_overflow);
34813368
3482 const overflowed = try cg.buildBinary(.l_or, low_overflowed, high_overflowed);3369 const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed);
34833370
3484 break :blk .{ result, overflowed };3371 break :blk .{ result, overflowed };
3485 },3372 },
...@@ -3517,15 +3404,15 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -3517,15 +3404,15 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
3517 // so just manually upcast it if required.3404 // so just manually upcast it if required.
3518 const casted_shift = try cg.buildConvert(base.ty.scalarType(zcu), shift);3405 const casted_shift = try cg.buildConvert(base.ty.scalarType(zcu), shift);
35193406
3520 const left = try cg.buildBinary(.sll, base, casted_shift);3407 const left = try cg.buildBinary(.OpShiftLeftLogical, base, casted_shift);
3521 const result = try cg.normalize(left, info);3408 const result = try cg.normalize(left, info);
35223409
3523 const right = switch (info.signedness) {3410 const right = switch (info.signedness) {
3524 .unsigned => try cg.buildBinary(.srl, result, casted_shift),3411 .unsigned => try cg.buildBinary(.OpShiftRightLogical, result, casted_shift),
3525 .signed => try cg.buildBinary(.sra, result, casted_shift),3412 .signed => try cg.buildBinary(.OpShiftRightArithmetic, result, casted_shift),
3526 };3413 };
35273414
3528 const overflowed = try cg.buildCmp(.i_ne, base, right);3415 const overflowed = try cg.buildCmp(.OpINotEqual, base, right);
3529 const ov = try cg.intFromBool(overflowed);3416 const ov = try cg.intFromBool(overflowed);
35303417
3531 const result_ty_id = try cg.resolveType(result_ty, .direct);3418 const result_ty_id = try cg.resolveType(result_ty, .direct);
...@@ -3957,19 +3844,19 @@ fn cmp(...@@ -3957,19 +3844,19 @@ fn cmp(
39573844
3958 return switch (op) {3845 return switch (op) {
3959 .eq => try cg.buildBinary(3846 .eq => try cg.buildBinary(
3960 .l_and,3847 .OpLogicalAnd,
3961 try cg.cmp(.eq, lhs_valid, rhs_valid),3848 try cg.cmp(.eq, lhs_valid, rhs_valid),
3962 try cg.buildBinary(3849 try cg.buildBinary(
3963 .l_or,3850 .OpLogicalOr,
3964 try cg.buildUnary(.l_not, lhs_valid),3851 try cg.buildUnary(.l_not, lhs_valid),
3965 try cg.cmp(.eq, lhs_pl, rhs_pl),3852 try cg.cmp(.eq, lhs_pl, rhs_pl),
3966 ),3853 ),
3967 ),3854 ),
3968 .neq => try cg.buildBinary(3855 .neq => try cg.buildBinary(
3969 .l_or,3856 .OpLogicalOr,
3970 try cg.cmp(.neq, lhs_valid, rhs_valid),3857 try cg.cmp(.neq, lhs_valid, rhs_valid),
3971 try cg.buildBinary(3858 try cg.buildBinary(
3972 .l_and,3859 .OpLogicalAnd,
3973 lhs_valid,3860 lhs_valid,
3974 try cg.cmp(.neq, lhs_pl, rhs_pl),3861 try cg.cmp(.neq, lhs_pl, rhs_pl),
3975 ),3862 ),
...@@ -3981,37 +3868,37 @@ fn cmp(...@@ -3981,37 +3868,37 @@ fn cmp(
3981 }3868 }
39823869
3983 const info = cg.arithmeticTypeInfo(scalar_ty);3870 const info = cg.arithmeticTypeInfo(scalar_ty);
3984 const pred: CmpPredicate = switch (info.class) {3871 const pred: Opcode = switch (info.class) {
3985 .composite_integer => unreachable, // TODO3872 .composite_integer => unreachable, // TODO
3986 .float => switch (op) {3873 .float => switch (op) {
3987 .eq => .f_oeq,3874 .eq => .OpFOrdEqual,
3988 .neq => .f_une,3875 .neq => .OpFUnordNotEqual,
3989 .lt => .f_olt,3876 .lt => .OpFOrdLessThan,
3990 .lte => .f_ole,3877 .lte => .OpFOrdLessThanEqual,
3991 .gt => .f_ogt,3878 .gt => .OpFOrdGreaterThan,
3992 .gte => .f_oge,3879 .gte => .OpFOrdGreaterThanEqual,
3993 },3880 },
3994 .bool => switch (op) {3881 .bool => switch (op) {
3995 .eq => .l_eq,3882 .eq => .OpLogicalEqual,
3996 .neq => .l_ne,3883 .neq => .OpLogicalNotEqual,
3997 else => unreachable,3884 else => unreachable,
3998 },3885 },
3999 .integer, .strange_integer => switch (info.signedness) {3886 .integer, .strange_integer => switch (info.signedness) {
4000 .signed => switch (op) {3887 .signed => switch (op) {
4001 .eq => .i_eq,3888 .eq => .OpIEqual,
4002 .neq => .i_ne,3889 .neq => .OpINotEqual,
4003 .lt => .s_lt,3890 .lt => .OpSLessThan,
4004 .lte => .s_le,3891 .lte => .OpSLessThanEqual,
4005 .gt => .s_gt,3892 .gt => .OpSGreaterThan,
4006 .gte => .s_ge,3893 .gte => .OpSGreaterThanEqual,
4007 },3894 },
4008 .unsigned => switch (op) {3895 .unsigned => switch (op) {
4009 .eq => .i_eq,3896 .eq => .OpIEqual,
4010 .neq => .i_ne,3897 .neq => .OpINotEqual,
4011 .lt => .u_lt,3898 .lt => .OpULessThan,
4012 .lte => .u_le,3899 .lte => .OpULessThanEqual,
4013 .gt => .u_gt,3900 .gt => .OpUGreaterThan,
4014 .gte => .u_ge,3901 .gte => .OpUGreaterThanEqual,
4015 },3902 },
4016 },3903 },
4017 };3904 };
...@@ -4312,12 +4199,12 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -4312,12 +4199,12 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4312 .ty = field_int_ty,4199 .ty = field_int_ty,
4313 .value = .{ .singleton = field_int_id },4200 .value = .{ .singleton = field_int_id },
4314 });4201 });
4315 const shifted = try cg.buildBinary(.sll, extended_int_conv, .{4202 const shifted = try cg.buildBinary(.OpShiftLeftLogical, extended_int_conv, .{
4316 .ty = backing_int_ty,4203 .ty = backing_int_ty,
4317 .value = .{ .singleton = shift_rhs },4204 .value = .{ .singleton = shift_rhs },
4318 });4205 });
4319 const running_int_tmp = try cg.buildBinary(4206 const running_int_tmp = try cg.buildBinary(
4320 .bit_or,4207 .OpBitwiseOr,
4321 .{ .ty = backing_int_ty, .value = .{ .singleton = running_int_id } },4208 .{ .ty = backing_int_ty, .value = .{ .singleton = running_int_id } },
4322 shifted,4209 shifted,
4323 );4210 );
...@@ -4770,17 +4657,20 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -4770,17 +4657,20 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4770 .@"struct" => switch (object_ty.containerLayout(zcu)) {4657 .@"struct" => switch (object_ty.containerLayout(zcu)) {
4771 .@"packed" => {4658 .@"packed" => {
4772 const struct_ty = zcu.typeToPackedStruct(object_ty).?;4659 const struct_ty = zcu.typeToPackedStruct(object_ty).?;
4660 const struct_backing_int_bits = cg.module.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0";
4773 const bit_offset = zcu.structPackedFieldBitOffset(struct_ty, field_index);4661 const bit_offset = zcu.structPackedFieldBitOffset(struct_ty, field_index);
4774 const bit_offset_id = try cg.constInt(.u16, bit_offset);4662 // We use the same int type the packed struct is backed by, because even though it would
4663 // be valid SPIR-V to use an smaller type like u16, some implementations like PoCL will complain.
4664 const bit_offset_id = try cg.constInt(object_ty, bit_offset);
4775 const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned;4665 const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned;
4776 const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu));4666 const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
4777 const field_int_ty = try pt.intType(signedness, field_bit_size);4667 const field_int_ty = try pt.intType(signedness, field_bit_size);
4778 const shift_lhs: Temporary = .{ .ty = object_ty, .value = .{ .singleton = object_id } };4668 const shift_lhs: Temporary = .{ .ty = object_ty, .value = .{ .singleton = object_id } };
4779 const shift = try cg.buildBinary(.srl, shift_lhs, .{ .ty = .u16, .value = .{ .singleton = bit_offset_id } });4669 const shift = try cg.buildBinary(.OpShiftRightLogical, shift_lhs, .{ .ty = object_ty, .value = .{ .singleton = bit_offset_id } });
4780 const mask_id = try cg.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);4670 const mask_id = try cg.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
4781 const masked = try cg.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });4671 const masked = try cg.buildBinary(.OpBitwiseAnd, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });
4782 const result_id = blk: {4672 const result_id = blk: {
4783 if (cg.module.backingIntBits(field_bit_size).@"0" == cg.module.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0")4673 if (cg.module.backingIntBits(field_bit_size).@"0" == struct_backing_int_bits)
4784 break :blk try cg.bitCast(field_int_ty, object_ty, try masked.materialize(cg));4674 break :blk try cg.bitCast(field_int_ty, object_ty, try masked.materialize(cg));
4785 const trunc = try cg.buildConvert(field_int_ty, masked);4675 const trunc = try cg.buildConvert(field_int_ty, masked);
4786 break :blk try trunc.materialize(cg);4676 break :blk try trunc.materialize(cg);
...@@ -4799,7 +4689,7 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -4799,7 +4689,7 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4799 const int_ty = try pt.intType(signedness, field_bit_size);4689 const int_ty = try pt.intType(signedness, field_bit_size);
4800 const mask_id = try cg.constInt(backing_int_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);4690 const mask_id = try cg.constInt(backing_int_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
4801 const masked = try cg.buildBinary(4691 const masked = try cg.buildBinary(
4802 .bit_and,4692 .OpBitwiseAnd,
4803 .{ .ty = backing_int_ty, .value = .{ .singleton = object_id } },4693 .{ .ty = backing_int_ty, .value = .{ .singleton = object_id } },
4804 .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } },4694 .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } },
4805 );4695 );
...@@ -4858,7 +4748,7 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {...@@ -4858,7 +4748,7 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
4858 const field_offset_id = try cg.constInt(.usize, field_offset);4748 const field_offset_id = try cg.constInt(.usize, field_offset);
4859 const field_ptr_tmp: Temporary = .init(.usize, field_ptr_int);4749 const field_ptr_tmp: Temporary = .init(.usize, field_ptr_int);
4860 const field_offset_tmp: Temporary = .init(.usize, field_offset_id);4750 const field_offset_tmp: Temporary = .init(.usize, field_offset_id);
4861 const result = try cg.buildBinary(.i_sub, field_ptr_tmp, field_offset_tmp);4751 const result = try cg.buildBinary(.OpISub, field_ptr_tmp, field_offset_tmp);
4862 break :base_ptr_int try result.materialize(cg);4752 break :base_ptr_int try result.materialize(cg);
4863 };4753 };
48644754
...@@ -4947,7 +4837,6 @@ fn alloc(...@@ -4947,7 +4837,6 @@ fn alloc(
4947 ty: Type,4837 ty: Type,
4948 options: AllocOptions,4838 options: AllocOptions,
4949) !Id {4839) !Id {
4950 const target = cg.module.zcu.getTarget();
4951 const ty_id = try cg.resolveType(ty, .indirect);4840 const ty_id = try cg.resolveType(ty, .indirect);
4952 const ptr_fn_ty_id = try cg.module.ptrType(ty_id, .function);4841 const ptr_fn_ty_id = try cg.module.ptrType(ty_id, .function);
49534842
...@@ -4961,20 +4850,7 @@ fn alloc(...@@ -4961,20 +4850,7 @@ fn alloc(
4961 .initializer = options.initializer,4850 .initializer = options.initializer,
4962 });4851 });
49634852
4964 switch (target.os.tag) {4853 return var_id;
4965 .vulkan, .opengl => return var_id,
4966 else => {},
4967 }
4968
4969 switch (options.storage_class) {
4970 .generic => {
4971 const ptr_gn_ty_id = try cg.module.ptrType(ty_id, .generic);
4972 // Convert to a generic pointer
4973 return cg.castToGeneric(ptr_gn_ty_id, var_id);
4974 },
4975 .function => return var_id,
4976 else => unreachable,
4977 }
4978}4854}
49794855
4980fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {4856fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
src/arch/spirv/Module.zig+5-2
...@@ -368,7 +368,10 @@ pub fn finalize(module: *Module, gpa: Allocator) ![]Word {...@@ -368,7 +368,10 @@ pub fn finalize(module: *Module, gpa: Allocator) ![]Word {
368 }368 }
369 if (target.cpu.arch == .spirv64) try module.addCapability(.int64);369 if (target.cpu.arch == .spirv64) try module.addCapability(.int64);
370 if (target.cpu.has(.spirv, .int64)) try module.addCapability(.int64);370 if (target.cpu.has(.spirv, .int64)) try module.addCapability(.int64);
371 if (target.cpu.has(.spirv, .float16)) try module.addCapability(.float16);371 if (target.cpu.has(.spirv, .float16)) {
372 if (target.os.tag == .opencl) try module.addExtension("cl_khr_fp16");
373 try module.addCapability(.float16);
374 }
372 if (target.cpu.has(.spirv, .float64)) try module.addCapability(.float64);375 if (target.cpu.has(.spirv, .float64)) try module.addCapability(.float64);
373 if (target.cpu.has(.spirv, .generic_pointer)) try module.addCapability(.generic_pointer);376 if (target.cpu.has(.spirv, .generic_pointer)) try module.addCapability(.generic_pointer);
374 if (target.cpu.has(.spirv, .vector16)) try module.addCapability(.vector16);377 if (target.cpu.has(.spirv, .vector16)) try module.addCapability(.vector16);
...@@ -920,7 +923,7 @@ pub fn debugString(module: *Module, string: []const u8) !Id {...@@ -920,7 +923,7 @@ pub fn debugString(module: *Module, string: []const u8) !Id {
920pub fn storageClass(module: *Module, as: std.builtin.AddressSpace) spec.StorageClass {923pub fn storageClass(module: *Module, as: std.builtin.AddressSpace) spec.StorageClass {
921 const target = module.zcu.getTarget();924 const target = module.zcu.getTarget();
922 return switch (as) {925 return switch (as) {
923 .generic => if (target.cpu.has(.spirv, .generic_pointer)) .generic else .function,926 .generic => .function,
924 .global => switch (target.os.tag) {927 .global => switch (target.os.tag) {
925 .opencl, .amdhsa => .cross_workgroup,928 .opencl, .amdhsa => .cross_workgroup,
926 else => .storage_buffer,929 else => .storage_buffer,