| ... | ... | @@ -431,15 +431,12 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id { |
| 431 | 431 | const zcu = cg.module.zcu; |
| 432 | 432 | const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val)); |
| 433 | 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)); |
| 435 | 434 | |
| 436 | 435 | const spv_decl_index = blk: { |
| 437 | 436 | const entry = try cg.module.uav_link.getOrPut(cg.module.gpa, .{ val, .function }); |
| 438 | 437 | if (entry.found_existing) { |
| 439 | 438 | try cg.addFunctionDep(entry.value_ptr.*, .function); |
| 440 | | |
| 441 | | const result_id = cg.module.declPtr(entry.value_ptr.*).result_id; |
| 442 | | return try cg.castToGeneric(decl_ptr_ty_id, result_id); |
| 439 | return cg.module.declPtr(entry.value_ptr.*).result_id; |
| 443 | 440 | } |
| 444 | 441 | |
| 445 | 442 | const spv_decl_index = try cg.module.allocDecl(.invocation_global); |
| ... | ... | @@ -520,7 +517,7 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id { |
| 520 | 517 | }); |
| 521 | 518 | } |
| 522 | 519 | |
| 523 | | return try cg.castToGeneric(decl_ptr_ty_id, result_id); |
| 520 | return result_id; |
| 524 | 521 | } |
| 525 | 522 | |
| 526 | 523 | fn 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 | 532 | } |
| 536 | 533 | } |
| 537 | 534 | |
| 538 | | fn 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 | 535 | /// Start a new SPIR-V block, Emits the label of the new block, and stores which |
| 554 | 536 | /// block we are currently generating. |
| 555 | 537 | /// 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 | 1191 | |
| 1210 | 1192 | const spv_decl_index = try cg.module.resolveNav(ip, nav_index); |
| 1211 | 1193 | const spv_decl = cg.module.declPtr(spv_decl_index); |
| 1212 | | |
| 1213 | | const decl_id = switch (spv_decl.kind) { |
| 1214 | | .func => unreachable, // TODO: Is this possible? |
| 1215 | | .global, .invocation_global => spv_decl.result_id, |
| 1216 | | }; |
| 1194 | assert(spv_decl.kind != .func); |
| 1217 | 1195 | |
| 1218 | 1196 | const storage_class = cg.module.storageClass(nav.getAddrspace()); |
| 1219 | 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 | 1199 | const nav_ty_id = try cg.resolveType(nav_ty, .indirect); |
| 1222 | 1200 | const decl_ptr_ty_id = try cg.module.ptrType(nav_ty_id, storage_class); |
| 1223 | 1201 | |
| 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 | 1202 | if (decl_ptr_ty_id != ty_id) { |
| 1230 | 1203 | // Differing pointer types, insert a cast. |
| 1231 | 1204 | const casted_ptr_id = cg.module.allocId(); |
| 1232 | 1205 | try cg.body.emit(cg.module.gpa, .OpBitcast, .{ |
| 1233 | 1206 | .id_result_type = ty_id, |
| 1234 | 1207 | .id_result = casted_ptr_id, |
| 1235 | | .operand = ptr_id, |
| 1208 | .operand = spv_decl.result_id, |
| 1236 | 1209 | }); |
| 1237 | 1210 | return casted_ptr_id; |
| 1238 | | } else { |
| 1239 | | return ptr_id; |
| 1240 | 1211 | } |
| 1212 | |
| 1213 | return spv_decl.result_id; |
| 1241 | 1214 | } |
| 1242 | 1215 | |
| 1243 | 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 | 2093 | return v.finalize(result_ty, results); |
| 2121 | 2094 | } |
| 2122 | 2095 | |
| 2123 | | const CmpPredicate = enum { |
| 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 | | |
| 2144 | | fn buildCmp(cg: *CodeGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary { |
| 2096 | fn buildCmp(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary { |
| 2145 | 2097 | const v = cg.vectorization(.{ lhs, rhs }); |
| 2146 | 2098 | const ops = v.components(); |
| 2147 | 2099 | const results = cg.module.allocIds(ops); |
| ... | ... | @@ -2153,27 +2105,6 @@ fn buildCmp(cg: *CodeGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !T |
| 2153 | 2105 | const op_lhs = try v.prepare(cg, lhs); |
| 2154 | 2106 | const op_rhs = try v.prepare(cg, rhs); |
| 2155 | 2107 | |
| 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 | 2108 | for (0..ops) |i| { |
| 2178 | 2109 | try cg.body.emitRaw(cg.module.gpa, opcode, 4); |
| 2179 | 2110 | cg.body.writeOperand(Id, op_result_ty_id); |
| ... | ... | @@ -2278,7 +2209,10 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary { |
| 2278 | 2209 | .log, |
| 2279 | 2210 | .log2, |
| 2280 | 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 | 2216 | else => unreachable, |
| 2283 | 2217 | }, |
| 2284 | 2218 | else => unreachable, |
| ... | ... | @@ -2298,40 +2232,8 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary { |
| 2298 | 2232 | return v.finalize(result_ty, results); |
| 2299 | 2233 | } |
| 2300 | 2234 | |
| 2301 | | const BinaryOp = enum { |
| 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 | | |
| 2332 | | fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary { |
| 2235 | fn buildBinary(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary { |
| 2333 | 2236 | const zcu = cg.module.zcu; |
| 2334 | | const target = cg.module.zcu.getTarget(); |
| 2335 | 2237 | |
| 2336 | 2238 | const v = cg.vectorization(.{ lhs, rhs }); |
| 2337 | 2239 | const ops = v.components(); |
| ... | ... | @@ -2344,73 +2246,12 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp |
| 2344 | 2246 | const op_lhs = try v.prepare(cg, lhs); |
| 2345 | 2247 | const op_rhs = try v.prepare(cg, rhs); |
| 2346 | 2248 | |
| 2347 | | if (switch (op) { |
| 2348 | | .i_add => .OpIAdd, |
| 2349 | | .f_add => .OpFAdd, |
| 2350 | | .i_sub => .OpISub, |
| 2351 | | .f_sub => .OpFSub, |
| 2352 | | .i_mul => .OpIMul, |
| 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 | | } |
| 2249 | for (0..ops) |i| { |
| 2250 | try cg.body.emitRaw(cg.module.gpa, opcode, 4); |
| 2251 | cg.body.writeOperand(Id, op_result_ty_id); |
| 2252 | cg.body.writeOperand(Id, results.at(i)); |
| 2253 | cg.body.writeOperand(Id, op_lhs.at(i)); |
| 2254 | cg.body.writeOperand(Id, op_rhs.at(i)); |
| 2414 | 2255 | } |
| 2415 | 2256 | |
| 2416 | 2257 | return v.finalize(result_ty, results); |
| ... | ... | @@ -2420,10 +2261,7 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp |
| 2420 | 2261 | /// or OpIMul and s_mul_hi or u_mul_hi on OpenCL. |
| 2421 | 2262 | fn buildWideMul( |
| 2422 | 2263 | cg: *CodeGen, |
| 2423 | | op: enum { |
| 2424 | | s_mul_extended, |
| 2425 | | u_mul_extended, |
| 2426 | | }, |
| 2264 | signedness: std.builtin.Signedness, |
| 2427 | 2265 | lhs: Temporary, |
| 2428 | 2266 | rhs: Temporary, |
| 2429 | 2267 | ) !struct { Temporary, Temporary } { |
| ... | ... | @@ -2450,9 +2288,9 @@ fn buildWideMul( |
| 2450 | 2288 | // OpUMulExtended. For these we will use the OpenCL s_mul_hi to compute the high-order bits |
| 2451 | 2289 | // instead. |
| 2452 | 2290 | const set = try cg.importExtendedSet(); |
| 2453 | | const overflow_inst: u32 = switch (op) { |
| 2454 | | .s_mul_extended => 160, // s_mul_hi |
| 2455 | | .u_mul_extended => 203, // u_mul_hi |
| 2291 | const overflow_inst: u32 = switch (signedness) { |
| 2292 | .signed => 160, // s_mul_hi |
| 2293 | .unsigned => 203, // u_mul_hi |
| 2456 | 2294 | }; |
| 2457 | 2295 | |
| 2458 | 2296 | for (0..ops) |i| { |
| ... | ... | @@ -2481,9 +2319,9 @@ fn buildWideMul( |
| 2481 | 2319 | })); |
| 2482 | 2320 | const op_result_ty_id = try cg.resolveType(op_result_ty, .direct); |
| 2483 | 2321 | |
| 2484 | | const opcode: Opcode = switch (op) { |
| 2485 | | .s_mul_extended => .OpSMulExtended, |
| 2486 | | .u_mul_extended => .OpUMulExtended, |
| 2322 | const opcode: Opcode = switch (signedness) { |
| 2323 | .signed => .OpSMulExtended, |
| 2324 | .unsigned => .OpUMulExtended, |
| 2487 | 2325 | }; |
| 2488 | 2326 | |
| 2489 | 2327 | for (0..ops) |i| { |
| ... | ... | @@ -2718,7 +2556,7 @@ fn convertToDirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id { |
| 2718 | 2556 | }; |
| 2719 | 2557 | |
| 2720 | 2558 | const result = try cg.buildCmp( |
| 2721 | | .i_ne, |
| 2559 | .OpINotEqual, |
| 2722 | 2560 | Temporary.init(operand_ty, operand_id), |
| 2723 | 2561 | Temporary.init(.u1, false_id), |
| 2724 | 2562 | ); |
| ... | ... | @@ -2817,9 +2655,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { |
| 2817 | 2655 | const air_tags = cg.air.instructions.items(.tag); |
| 2818 | 2656 | const maybe_result_id: ?Id = switch (air_tags[@intFromEnum(inst)]) { |
| 2819 | 2657 | // zig fmt: off |
| 2820 | | .add, .add_wrap, .add_optimized => try cg.airArithOp(inst, .f_add, .i_add, .i_add), |
| 2821 | | .sub, .sub_wrap, .sub_optimized => try cg.airArithOp(inst, .f_sub, .i_sub, .i_sub), |
| 2822 | | .mul, .mul_wrap, .mul_optimized => try cg.airArithOp(inst, .f_mul, .i_mul, .i_mul), |
| 2658 | .add, .add_wrap, .add_optimized => try cg.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd), |
| 2659 | .sub, .sub_wrap, .sub_optimized => try cg.airArithOp(inst, .OpFSub, .OpISub, .OpISub), |
| 2660 | .mul, .mul_wrap, .mul_optimized => try cg.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul), |
| 2823 | 2661 | |
| 2824 | 2662 | .sqrt => try cg.airUnOpSimple(inst, .sqrt), |
| 2825 | 2663 | .sin => try cg.airUnOpSimple(inst, .sin), |
| ... | ... | @@ -2837,15 +2675,15 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { |
| 2837 | 2675 | .trunc_float => try cg.airUnOpSimple(inst, .trunc), |
| 2838 | 2676 | .neg, .neg_optimized => try cg.airUnOpSimple(inst, .f_neg), |
| 2839 | 2677 | |
| 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 | 2679 | .div_floor, .div_floor_optimized => try cg.airDivFloor(inst), |
| 2842 | 2680 | .div_trunc, .div_trunc_optimized => try cg.airDivTrunc(inst), |
| 2843 | 2681 | |
| 2844 | | .rem, .rem_optimized => try cg.airArithOp(inst, .f_rem, .s_rem, .u_mod), |
| 2845 | | .mod, .mod_optimized => try cg.airArithOp(inst, .f_mod, .s_mod, .u_mod), |
| 2682 | .rem, .rem_optimized => try cg.airArithOp(inst, .OpFRem, .OpSRem, .OpUMod), |
| 2683 | .mod, .mod_optimized => try cg.airArithOp(inst, .OpFMod, .OpSMod, .OpUMod), |
| 2846 | 2684 | |
| 2847 | | .add_with_overflow => try cg.airAddSubOverflow(inst, .i_add, .u_lt, .s_lt), |
| 2848 | | .sub_with_overflow => try cg.airAddSubOverflow(inst, .i_sub, .u_gt, .s_gt), |
| 2685 | .add_with_overflow => try cg.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan), |
| 2686 | .sub_with_overflow => try cg.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan), |
| 2849 | 2687 | .mul_with_overflow => try cg.airMulOverflow(inst), |
| 2850 | 2688 | .shl_with_overflow => try cg.airShlOverflow(inst), |
| 2851 | 2689 | |
| ... | ... | @@ -2864,14 +2702,14 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { |
| 2864 | 2702 | .ptr_add => try cg.airPtrAdd(inst), |
| 2865 | 2703 | .ptr_sub => try cg.airPtrSub(inst), |
| 2866 | 2704 | |
| 2867 | | .bit_and => try cg.airBinOpSimple(inst, .bit_and), |
| 2868 | | .bit_or => try cg.airBinOpSimple(inst, .bit_or), |
| 2869 | | .xor => try cg.airBinOpSimple(inst, .bit_xor), |
| 2870 | | .bool_and => try cg.airBinOpSimple(inst, .l_and), |
| 2871 | | .bool_or => try cg.airBinOpSimple(inst, .l_or), |
| 2705 | .bit_and => try cg.airBinOpSimple(inst, .OpBitwiseAnd), |
| 2706 | .bit_or => try cg.airBinOpSimple(inst, .OpBitwiseOr), |
| 2707 | .xor => try cg.airBinOpSimple(inst, .OpBitwiseXor), |
| 2708 | .bool_and => try cg.airBinOpSimple(inst, .OpLogicalAnd), |
| 2709 | .bool_or => try cg.airBinOpSimple(inst, .OpLogicalOr), |
| 2872 | 2710 | |
| 2873 | | .shl, .shl_exact => try cg.airShift(inst, .sll, .sll), |
| 2874 | | .shr, .shr_exact => try cg.airShift(inst, .srl, .sra), |
| 2711 | .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical), |
| 2712 | .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic), |
| 2875 | 2713 | |
| 2876 | 2714 | .min => try cg.airMinMax(inst, .min), |
| 2877 | 2715 | .max => try cg.airMinMax(inst, .max), |
| ... | ... | @@ -2983,7 +2821,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { |
| 2983 | 2821 | try cg.inst_results.putNoClobber(gpa, inst, result_id); |
| 2984 | 2822 | } |
| 2985 | 2823 | |
| 2986 | | fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: BinaryOp) !?Id { |
| 2824 | fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id { |
| 2987 | 2825 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2988 | 2826 | const lhs = try cg.temporary(bin_op.lhs); |
| 2989 | 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 | 2830 | return try result.materialize(cg); |
| 2993 | 2831 | } |
| 2994 | 2832 | |
| 2995 | | fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: BinaryOp, signed: BinaryOp) !?Id { |
| 2833 | fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id { |
| 2996 | 2834 | const zcu = cg.module.zcu; |
| 2997 | 2835 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2998 | 2836 | |
| ... | ... | @@ -3042,28 +2880,77 @@ fn airMinMax(cg: *CodeGen, inst: Air.Inst.Index, op: MinMax) !?Id { |
| 3042 | 2880 | } |
| 3043 | 2881 | |
| 3044 | 2882 | fn minMax(cg: *CodeGen, lhs: Temporary, rhs: Temporary, op: MinMax) !Temporary { |
| 2883 | const zcu = cg.module.zcu; |
| 2884 | const target = zcu.getTarget(); |
| 3045 | 2885 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| 3046 | 2886 | |
| 3047 | | const binop: BinaryOp = switch (info.class) { |
| 3048 | | .float => switch (op) { |
| 3049 | | .min => .f_min, |
| 3050 | | .max => .f_max, |
| 2887 | const v = cg.vectorization(.{ lhs, rhs }); |
| 2888 | const ops = v.components(); |
| 2889 | const results = cg.module.allocIds(ops); |
| 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) { |
| 3053 | | .signed => switch (op) { |
| 3054 | | .min => .s_min, |
| 3055 | | .max => .s_max, |
| 2919 | .vulkan, .opengl => switch (info.class) { |
| 2920 | .float => switch (op) { |
| 2921 | .min => 37, // FMin |
| 2922 | .max => 40, // FMax |
| 3056 | 2923 | }, |
| 3057 | | .unsigned => switch (op) { |
| 3058 | | .min => .u_min, |
| 3059 | | .max => .u_max, |
| 2924 | .integer, |
| 2925 | .strange_integer, |
| 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, // TODO |
| 3063 | | .bool => unreachable, |
| 2939 | else => unreachable, |
| 3064 | 2940 | }; |
| 3065 | 2941 | |
| 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 | } |
| 3068 | 2955 | |
| 3069 | 2956 | /// This function normalizes values to a canonical representation |
| ... | ... | @@ -3083,14 +2970,14 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar |
| 3083 | 2970 | .unsigned => { |
| 3084 | 2971 | const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1; |
| 3085 | 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 | 2975 | .signed => { |
| 3089 | 2976 | // Shift left and right so that we can copy the sight bit that way. |
| 3090 | 2977 | const shift_amt_id = try cg.constInt(ty.scalarType(zcu), info.backing_bits - info.bits); |
| 3091 | 2978 | const shift_amt: Temporary = .init(ty.scalarType(zcu), shift_amt_id); |
| 3092 | | const left = try cg.buildBinary(.sll, value, shift_amt); |
| 3093 | | return try cg.buildBinary(.sra, left, shift_amt); |
| 2979 | const left = try cg.buildBinary(.OpShiftLeftLogical, value, 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 | 2995 | .integer, .strange_integer => { |
| 3109 | 2996 | switch (info.signedness) { |
| 3110 | 2997 | .unsigned => { |
| 3111 | | const result = try cg.buildBinary(.u_div, lhs, rhs); |
| 2998 | const result = try cg.buildBinary(.OpUDiv, lhs, rhs); |
| 3112 | 2999 | return try result.materialize(cg); |
| 3113 | 3000 | }, |
| 3114 | 3001 | .signed => {}, |
| ... | ... | @@ -3118,26 +3005,26 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3118 | 3005 | // (a / b) - (a % b != 0 && a < 0 != b < 0); |
| 3119 | 3006 | // There shouldn't be any overflow issues. |
| 3120 | 3007 | |
| 3121 | | const div = try cg.buildBinary(.s_div, lhs, rhs); |
| 3122 | | const rem = try cg.buildBinary(.s_rem, lhs, rhs); |
| 3008 | const div = try cg.buildBinary(.OpSDiv, lhs, rhs); |
| 3009 | const rem = try cg.buildBinary(.OpSRem, lhs, rhs); |
| 3123 | 3010 | |
| 3124 | 3011 | const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0)); |
| 3125 | 3012 | |
| 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); |
| 3127 | 3014 | |
| 3128 | 3015 | const result_negative = try cg.buildCmp( |
| 3129 | | .l_ne, |
| 3130 | | try cg.buildCmp(.s_lt, lhs, zero), |
| 3131 | | try cg.buildCmp(.s_lt, rhs, zero), |
| 3016 | .OpLogicalNotEqual, |
| 3017 | try cg.buildCmp(.OpSLessThan, lhs, zero), |
| 3018 | try cg.buildCmp(.OpSLessThan, rhs, zero), |
| 3132 | 3019 | ); |
| 3133 | 3020 | const rem_is_not_zero_and_result_is_negative = try cg.buildBinary( |
| 3134 | | .l_and, |
| 3021 | .OpLogicalAnd, |
| 3135 | 3022 | rem_is_not_zero, |
| 3136 | 3023 | result_negative, |
| 3137 | 3024 | ); |
| 3138 | 3025 | |
| 3139 | 3026 | const result = try cg.buildBinary( |
| 3140 | | .i_sub, |
| 3027 | .OpISub, |
| 3141 | 3028 | div, |
| 3142 | 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 | 3032 | return try result.materialize(cg); |
| 3146 | 3033 | }, |
| 3147 | 3034 | .float => { |
| 3148 | | const div = try cg.buildBinary(.f_div, lhs, rhs); |
| 3035 | const div = try cg.buildBinary(.OpFDiv, lhs, rhs); |
| 3149 | 3036 | const result = try cg.buildUnary(.floor, div); |
| 3150 | 3037 | return try result.materialize(cg); |
| 3151 | 3038 | }, |
| ... | ... | @@ -3164,16 +3051,16 @@ fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3164 | 3051 | .composite_integer => unreachable, // TODO |
| 3165 | 3052 | .integer, .strange_integer => switch (info.signedness) { |
| 3166 | 3053 | .unsigned => { |
| 3167 | | const result = try cg.buildBinary(.u_div, lhs, rhs); |
| 3054 | const result = try cg.buildBinary(.OpUDiv, lhs, rhs); |
| 3168 | 3055 | return try result.materialize(cg); |
| 3169 | 3056 | }, |
| 3170 | 3057 | .signed => { |
| 3171 | | const result = try cg.buildBinary(.s_div, lhs, rhs); |
| 3058 | const result = try cg.buildBinary(.OpSDiv, lhs, rhs); |
| 3172 | 3059 | return try result.materialize(cg); |
| 3173 | 3060 | }, |
| 3174 | 3061 | }, |
| 3175 | 3062 | .float => { |
| 3176 | | const div = try cg.buildBinary(.f_div, lhs, rhs); |
| 3063 | const div = try cg.buildBinary(.OpFDiv, lhs, rhs); |
| 3177 | 3064 | const result = try cg.buildUnary(.trunc, div); |
| 3178 | 3065 | return try result.materialize(cg); |
| 3179 | 3066 | }, |
| ... | ... | @@ -3191,9 +3078,9 @@ fn airUnOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: UnaryOp) !?Id { |
| 3191 | 3078 | fn airArithOp( |
| 3192 | 3079 | cg: *CodeGen, |
| 3193 | 3080 | inst: Air.Inst.Index, |
| 3194 | | comptime fop: BinaryOp, |
| 3195 | | comptime sop: BinaryOp, |
| 3196 | | comptime uop: BinaryOp, |
| 3081 | comptime fop: Opcode, |
| 3082 | comptime sop: Opcode, |
| 3083 | comptime uop: Opcode, |
| 3197 | 3084 | ) !?Id { |
| 3198 | 3085 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3199 | 3086 | |
| ... | ... | @@ -3253,11 +3140,11 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary { |
| 3253 | 3140 | fn airAddSubOverflow( |
| 3254 | 3141 | cg: *CodeGen, |
| 3255 | 3142 | inst: Air.Inst.Index, |
| 3256 | | comptime add: BinaryOp, |
| 3257 | | comptime ucmp: CmpPredicate, |
| 3258 | | comptime scmp: CmpPredicate, |
| 3143 | comptime add: Opcode, |
| 3144 | u_opcode: Opcode, |
| 3145 | s_opcode: Opcode, |
| 3259 | 3146 | ) !?Id { |
| 3260 | | _ = scmp; |
| 3147 | _ = s_opcode; |
| 3261 | 3148 | // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers, |
| 3262 | 3149 | // there is in both cases only one extra operation required. For signed operations, |
| 3263 | 3150 | // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually |
| ... | ... | @@ -3285,7 +3172,7 @@ fn airAddSubOverflow( |
| 3285 | 3172 | const overflowed = switch (info.signedness) { |
| 3286 | 3173 | // Overflow happened if the result is smaller than either of the operands. It doesn't matter which. |
| 3287 | 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 | 3176 | // For signed operations, we check the signs of the operands and the result. |
| 3290 | 3177 | .signed => blk: { |
| 3291 | 3178 | // Signed overflow detection using the sign bits of the operands and the result. |
| ... | ... | @@ -3297,19 +3184,19 @@ fn airAddSubOverflow( |
| 3297 | 3184 | // (sign(a) != sign(b)) && (sign(a) != sign(result)) |
| 3298 | 3185 | const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0)); |
| 3299 | 3186 | |
| 3300 | | const lhs_is_neg = try cg.buildCmp(.s_lt, lhs, zero); |
| 3301 | | const rhs_is_neg = try cg.buildCmp(.s_lt, rhs, zero); |
| 3302 | | const result_is_neg = try cg.buildCmp(.s_lt, result, zero); |
| 3187 | const lhs_is_neg = try cg.buildCmp(.OpSLessThan, lhs, zero); |
| 3188 | const rhs_is_neg = try cg.buildCmp(.OpSLessThan, rhs, zero); |
| 3189 | const result_is_neg = try cg.buildCmp(.OpSLessThan, result, zero); |
| 3303 | 3190 | |
| 3304 | | const signs_match = try cg.buildCmp(.l_eq, lhs_is_neg, rhs_is_neg); |
| 3305 | | const result_sign_differs = try cg.buildCmp(.l_ne, lhs_is_neg, result_is_neg); |
| 3191 | const signs_match = try cg.buildCmp(.OpLogicalEqual, lhs_is_neg, rhs_is_neg); |
| 3192 | const result_sign_differs = try cg.buildCmp(.OpLogicalNotEqual, lhs_is_neg, result_is_neg); |
| 3306 | 3193 | |
| 3307 | | const overflow_condition = if (add == .i_add) |
| 3194 | const overflow_condition = if (add == .OpIAdd) |
| 3308 | 3195 | signs_match |
| 3309 | | else // .i_sub |
| 3196 | else // .OpISub |
| 3310 | 3197 | try cg.buildUnary(.l_not, signs_match); |
| 3311 | 3198 | |
| 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 | }; |
| 3315 | 3202 | |
| ... | ... | @@ -3361,23 +3248,23 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3361 | 3248 | const casted_lhs = try cg.buildConvert(op_ty, lhs); |
| 3362 | 3249 | const casted_rhs = try cg.buildConvert(op_ty, rhs); |
| 3363 | 3250 | |
| 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); |
| 3365 | 3252 | |
| 3366 | 3253 | const low_bits = try cg.buildConvert(lhs.ty, full_result); |
| 3367 | 3254 | const result = try cg.normalize(low_bits, info); |
| 3368 | 3255 | |
| 3369 | 3256 | // Shift the result bits away to get the overflow bits. |
| 3370 | 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); |
| 3372 | 3259 | |
| 3373 | 3260 | // Directly check if its zero in the op_ty without converting first. |
| 3374 | 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); |
| 3376 | 3263 | |
| 3377 | 3264 | break :blk .{ result, overflowed }; |
| 3378 | 3265 | } |
| 3379 | 3266 | |
| 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); |
| 3381 | 3268 | |
| 3382 | 3269 | // Truncate the result, if required. |
| 3383 | 3270 | const result = try cg.normalize(low_bits, info); |
| ... | ... | @@ -3386,17 +3273,17 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3386 | 3273 | // high bits of the low word of the result (those outside the range of the |
| 3387 | 3274 | // int) are nonzero. |
| 3388 | 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); |
| 3390 | 3277 | |
| 3391 | 3278 | // If no overflow bits in low_bits, no extra work needs to be done. |
| 3392 | 3279 | if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed }; |
| 3393 | 3280 | |
| 3394 | 3281 | // Shift the result bits away to get the overflow bits. |
| 3395 | 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); |
| 3397 | | const low_overflowed = try cg.buildCmp(.i_ne, zero, low_overflow); |
| 3283 | const low_overflow = try cg.buildBinary(.OpShiftRightLogical, low_bits, shift); |
| 3284 | const low_overflowed = try cg.buildCmp(.OpINotEqual, zero, low_overflow); |
| 3398 | 3285 | |
| 3399 | | const overflowed = try cg.buildBinary(.l_or, low_overflowed, high_overflowed); |
| 3286 | const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed); |
| 3400 | 3287 | |
| 3401 | 3288 | break :blk .{ result, overflowed }; |
| 3402 | 3289 | }, |
| ... | ... | @@ -3412,16 +3299,16 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3412 | 3299 | // (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0) |
| 3413 | 3300 | |
| 3414 | 3301 | const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0)); |
| 3415 | | const lhs_negative = try cg.buildCmp(.s_lt, lhs, zero); |
| 3416 | | const rhs_negative = try cg.buildCmp(.s_lt, rhs, zero); |
| 3417 | | const lhs_positive = try cg.buildCmp(.s_gt, lhs, zero); |
| 3418 | | const rhs_positive = try cg.buildCmp(.s_gt, rhs, zero); |
| 3302 | const lhs_negative = try cg.buildCmp(.OpSLessThan, lhs, zero); |
| 3303 | const rhs_negative = try cg.buildCmp(.OpSLessThan, rhs, zero); |
| 3304 | const lhs_positive = try cg.buildCmp(.OpSGreaterThan, lhs, zero); |
| 3305 | const rhs_positive = try cg.buildCmp(.OpSGreaterThan, rhs, zero); |
| 3419 | 3306 | |
| 3420 | 3307 | // Set to `true` if we expect -1. |
| 3421 | 3308 | const expected_overflow_bit = try cg.buildBinary( |
| 3422 | | .l_or, |
| 3423 | | try cg.buildBinary(.l_and, lhs_positive, rhs_negative), |
| 3424 | | try cg.buildBinary(.l_and, lhs_negative, rhs_positive), |
| 3309 | .OpLogicalOr, |
| 3310 | try cg.buildCmp(.OpLogicalAnd, lhs_positive, rhs_negative), |
| 3311 | try cg.buildCmp(.OpLogicalAnd, lhs_negative, rhs_positive), |
| 3425 | 3312 | ); |
| 3426 | 3313 | |
| 3427 | 3314 | if (maybe_op_ty_bits) |op_ty_bits| { |
| ... | ... | @@ -3430,7 +3317,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3430 | 3317 | const casted_lhs = try cg.buildConvert(op_ty, lhs); |
| 3431 | 3318 | const casted_rhs = try cg.buildConvert(op_ty, rhs); |
| 3432 | 3319 | |
| 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); |
| 3434 | 3321 | |
| 3435 | 3322 | // Truncate to the result type. |
| 3436 | 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 | 3330 | const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits - 1)); |
| 3444 | 3331 | // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set |
| 3445 | 3332 | // for negative cases. |
| 3446 | | const overflow = try cg.buildBinary(.sra, full_result, shift); |
| 3333 | const overflow = try cg.buildBinary(.OpShiftRightArithmetic, full_result, shift); |
| 3447 | 3334 | |
| 3448 | 3335 | const long_all_set: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, -1)); |
| 3449 | 3336 | const long_zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0)); |
| 3450 | 3337 | const mask = try cg.buildSelect(expected_overflow_bit, long_all_set, long_zero); |
| 3451 | 3338 | |
| 3452 | | const overflowed = try cg.buildCmp(.i_ne, mask, overflow); |
| 3339 | const overflowed = try cg.buildCmp(.OpINotEqual, mask, overflow); |
| 3453 | 3340 | |
| 3454 | 3341 | break :blk .{ result, overflowed }; |
| 3455 | 3342 | } |
| 3456 | 3343 | |
| 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); |
| 3458 | 3345 | |
| 3459 | 3346 | // Truncate result if required. |
| 3460 | 3347 | const result = try cg.normalize(low_bits, info); |
| ... | ... | @@ -3465,7 +3352,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3465 | 3352 | // Like with unsigned, overflow happened if high_bits are not the ones we expect, |
| 3466 | 3353 | // and we also need to check some ones from the low bits. |
| 3467 | 3354 | |
| 3468 | | const high_overflowed = try cg.buildCmp(.i_ne, mask, high_bits); |
| 3355 | const high_overflowed = try cg.buildCmp(.OpINotEqual, mask, high_bits); |
| 3469 | 3356 | |
| 3470 | 3357 | // If no overflow bits in low_bits, no extra work needs to be done. |
| 3471 | 3358 | // 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 | 3363 | const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits - 1)); |
| 3477 | 3364 | // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set |
| 3478 | 3365 | // for negative cases. |
| 3479 | | const low_overflow = try cg.buildBinary(.sra, low_bits, shift); |
| 3480 | | const low_overflowed = try cg.buildCmp(.i_ne, mask, low_overflow); |
| 3366 | const low_overflow = try cg.buildBinary(.OpShiftRightArithmetic, low_bits, shift); |
| 3367 | const low_overflowed = try cg.buildCmp(.OpINotEqual, mask, low_overflow); |
| 3481 | 3368 | |
| 3482 | | const overflowed = try cg.buildBinary(.l_or, low_overflowed, high_overflowed); |
| 3369 | const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed); |
| 3483 | 3370 | |
| 3484 | 3371 | break :blk .{ result, overflowed }; |
| 3485 | 3372 | }, |
| ... | ... | @@ -3517,15 +3404,15 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3517 | 3404 | // so just manually upcast it if required. |
| 3518 | 3405 | const casted_shift = try cg.buildConvert(base.ty.scalarType(zcu), shift); |
| 3519 | 3406 | |
| 3520 | | const left = try cg.buildBinary(.sll, base, casted_shift); |
| 3407 | const left = try cg.buildBinary(.OpShiftLeftLogical, base, casted_shift); |
| 3521 | 3408 | const result = try cg.normalize(left, info); |
| 3522 | 3409 | |
| 3523 | 3410 | const right = switch (info.signedness) { |
| 3524 | | .unsigned => try cg.buildBinary(.srl, result, casted_shift), |
| 3525 | | .signed => try cg.buildBinary(.sra, result, casted_shift), |
| 3411 | .unsigned => try cg.buildBinary(.OpShiftRightLogical, result, casted_shift), |
| 3412 | .signed => try cg.buildBinary(.OpShiftRightArithmetic, result, casted_shift), |
| 3526 | 3413 | }; |
| 3527 | 3414 | |
| 3528 | | const overflowed = try cg.buildCmp(.i_ne, base, right); |
| 3415 | const overflowed = try cg.buildCmp(.OpINotEqual, base, right); |
| 3529 | 3416 | const ov = try cg.intFromBool(overflowed); |
| 3530 | 3417 | |
| 3531 | 3418 | const result_ty_id = try cg.resolveType(result_ty, .direct); |
| ... | ... | @@ -3957,19 +3844,19 @@ fn cmp( |
| 3957 | 3844 | |
| 3958 | 3845 | return switch (op) { |
| 3959 | 3846 | .eq => try cg.buildBinary( |
| 3960 | | .l_and, |
| 3847 | .OpLogicalAnd, |
| 3961 | 3848 | try cg.cmp(.eq, lhs_valid, rhs_valid), |
| 3962 | 3849 | try cg.buildBinary( |
| 3963 | | .l_or, |
| 3850 | .OpLogicalOr, |
| 3964 | 3851 | try cg.buildUnary(.l_not, lhs_valid), |
| 3965 | 3852 | try cg.cmp(.eq, lhs_pl, rhs_pl), |
| 3966 | 3853 | ), |
| 3967 | 3854 | ), |
| 3968 | 3855 | .neq => try cg.buildBinary( |
| 3969 | | .l_or, |
| 3856 | .OpLogicalOr, |
| 3970 | 3857 | try cg.cmp(.neq, lhs_valid, rhs_valid), |
| 3971 | 3858 | try cg.buildBinary( |
| 3972 | | .l_and, |
| 3859 | .OpLogicalAnd, |
| 3973 | 3860 | lhs_valid, |
| 3974 | 3861 | try cg.cmp(.neq, lhs_pl, rhs_pl), |
| 3975 | 3862 | ), |
| ... | ... | @@ -3981,37 +3868,37 @@ fn cmp( |
| 3981 | 3868 | } |
| 3982 | 3869 | |
| 3983 | 3870 | const info = cg.arithmeticTypeInfo(scalar_ty); |
| 3984 | | const pred: CmpPredicate = switch (info.class) { |
| 3871 | const pred: Opcode = switch (info.class) { |
| 3985 | 3872 | .composite_integer => unreachable, // TODO |
| 3986 | 3873 | .float => switch (op) { |
| 3987 | | .eq => .f_oeq, |
| 3988 | | .neq => .f_une, |
| 3989 | | .lt => .f_olt, |
| 3990 | | .lte => .f_ole, |
| 3991 | | .gt => .f_ogt, |
| 3992 | | .gte => .f_oge, |
| 3874 | .eq => .OpFOrdEqual, |
| 3875 | .neq => .OpFUnordNotEqual, |
| 3876 | .lt => .OpFOrdLessThan, |
| 3877 | .lte => .OpFOrdLessThanEqual, |
| 3878 | .gt => .OpFOrdGreaterThan, |
| 3879 | .gte => .OpFOrdGreaterThanEqual, |
| 3993 | 3880 | }, |
| 3994 | 3881 | .bool => switch (op) { |
| 3995 | | .eq => .l_eq, |
| 3996 | | .neq => .l_ne, |
| 3882 | .eq => .OpLogicalEqual, |
| 3883 | .neq => .OpLogicalNotEqual, |
| 3997 | 3884 | else => unreachable, |
| 3998 | 3885 | }, |
| 3999 | 3886 | .integer, .strange_integer => switch (info.signedness) { |
| 4000 | 3887 | .signed => switch (op) { |
| 4001 | | .eq => .i_eq, |
| 4002 | | .neq => .i_ne, |
| 4003 | | .lt => .s_lt, |
| 4004 | | .lte => .s_le, |
| 4005 | | .gt => .s_gt, |
| 4006 | | .gte => .s_ge, |
| 3888 | .eq => .OpIEqual, |
| 3889 | .neq => .OpINotEqual, |
| 3890 | .lt => .OpSLessThan, |
| 3891 | .lte => .OpSLessThanEqual, |
| 3892 | .gt => .OpSGreaterThan, |
| 3893 | .gte => .OpSGreaterThanEqual, |
| 4007 | 3894 | }, |
| 4008 | 3895 | .unsigned => switch (op) { |
| 4009 | | .eq => .i_eq, |
| 4010 | | .neq => .i_ne, |
| 4011 | | .lt => .u_lt, |
| 4012 | | .lte => .u_le, |
| 4013 | | .gt => .u_gt, |
| 4014 | | .gte => .u_ge, |
| 3896 | .eq => .OpIEqual, |
| 3897 | .neq => .OpINotEqual, |
| 3898 | .lt => .OpULessThan, |
| 3899 | .lte => .OpULessThanEqual, |
| 3900 | .gt => .OpUGreaterThan, |
| 3901 | .gte => .OpUGreaterThanEqual, |
| 4015 | 3902 | }, |
| 4016 | 3903 | }, |
| 4017 | 3904 | }; |
| ... | ... | @@ -4312,12 +4199,12 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4312 | 4199 | .ty = field_int_ty, |
| 4313 | 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 | 4203 | .ty = backing_int_ty, |
| 4317 | 4204 | .value = .{ .singleton = shift_rhs }, |
| 4318 | 4205 | }); |
| 4319 | 4206 | const running_int_tmp = try cg.buildBinary( |
| 4320 | | .bit_or, |
| 4207 | .OpBitwiseOr, |
| 4321 | 4208 | .{ .ty = backing_int_ty, .value = .{ .singleton = running_int_id } }, |
| 4322 | 4209 | shifted, |
| 4323 | 4210 | ); |
| ... | ... | @@ -4770,17 +4657,20 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4770 | 4657 | .@"struct" => switch (object_ty.containerLayout(zcu)) { |
| 4771 | 4658 | .@"packed" => { |
| 4772 | 4659 | const struct_ty = zcu.typeToPackedStruct(object_ty).?; |
| 4660 | const struct_backing_int_bits = cg.module.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0"; |
| 4773 | 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 | 4665 | const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned; |
| 4776 | 4666 | const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu)); |
| 4777 | 4667 | const field_int_ty = try pt.intType(signedness, field_bit_size); |
| 4778 | 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 | 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 | 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 | 4674 | break :blk try cg.bitCast(field_int_ty, object_ty, try masked.materialize(cg)); |
| 4785 | 4675 | const trunc = try cg.buildConvert(field_int_ty, masked); |
| 4786 | 4676 | break :blk try trunc.materialize(cg); |
| ... | ... | @@ -4799,7 +4689,7 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4799 | 4689 | const int_ty = try pt.intType(signedness, field_bit_size); |
| 4800 | 4690 | const mask_id = try cg.constInt(backing_int_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1); |
| 4801 | 4691 | const masked = try cg.buildBinary( |
| 4802 | | .bit_and, |
| 4692 | .OpBitwiseAnd, |
| 4803 | 4693 | .{ .ty = backing_int_ty, .value = .{ .singleton = object_id } }, |
| 4804 | 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 | 4748 | const field_offset_id = try cg.constInt(.usize, field_offset); |
| 4859 | 4749 | const field_ptr_tmp: Temporary = .init(.usize, field_ptr_int); |
| 4860 | 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 | 4752 | break :base_ptr_int try result.materialize(cg); |
| 4863 | 4753 | }; |
| 4864 | 4754 | |
| ... | ... | @@ -4947,7 +4837,6 @@ fn alloc( |
| 4947 | 4837 | ty: Type, |
| 4948 | 4838 | options: AllocOptions, |
| 4949 | 4839 | ) !Id { |
| 4950 | | const target = cg.module.zcu.getTarget(); |
| 4951 | 4840 | const ty_id = try cg.resolveType(ty, .indirect); |
| 4952 | 4841 | const ptr_fn_ty_id = try cg.module.ptrType(ty_id, .function); |
| 4953 | 4842 | |
| ... | ... | @@ -4961,20 +4850,7 @@ fn alloc( |
| 4961 | 4850 | .initializer = options.initializer, |
| 4962 | 4851 | }); |
| 4963 | 4852 | |
| 4964 | | switch (target.os.tag) { |
| 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 | | } |
| 4853 | return var_id; |
| 4978 | 4854 | } |
| 4979 | 4855 | |
| 4980 | 4856 | fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |