| ... | ... | @@ -5,6 +5,7 @@ const Signedness = std.lang.Signedness; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const log = std.log.scoped(.codegen); |
| 7 | 7 | |
| 8 | const builtin = @import("builtin"); |
| 8 | 9 | const link = @import("../../link.zig"); |
| 9 | 10 | const codegen = @import("../../codegen.zig"); |
| 10 | 11 | const Zcu = @import("../../Zcu.zig"); |
| ... | ... | @@ -511,8 +512,8 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 511 | 512 | else => {}, |
| 512 | 513 | } |
| 513 | 514 | |
| 514 | | if (std.meta.stringToEnum(spec.BuiltIn, nav.fqn.toSlice(ip))) |builtin| { |
| 515 | | try cg.module.decorate(result_id, .{ .built_in = .{ .built_in = builtin } }); |
| 515 | if (std.meta.stringToEnum(spec.BuiltIn, nav.fqn.toSlice(ip))) |built_in| { |
| 516 | try cg.module.decorate(result_id, .{ .built_in = .{ .built_in = built_in } }); |
| 516 | 517 | } |
| 517 | 518 | |
| 518 | 519 | try cg.module.debugName(result_id, nav.fqn.toSlice(ip)); |
| ... | ... | @@ -956,6 +957,7 @@ fn constBool(cg: *CodeGen, value: bool, repr: Repr) !Id { |
| 956 | 957 | /// This function, unlike Module.constInt, takes care to bitcast |
| 957 | 958 | /// the value to an unsigned int first for Kernels. |
| 958 | 959 | fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id { |
| 960 | const gpa = cg.module.gpa; |
| 959 | 961 | const zcu = cg.module.zcu; |
| 960 | 962 | const target = cg.module.zcu.getTarget(); |
| 961 | 963 | const scalar_ty = ty.scalarType(zcu); |
| ... | ... | @@ -975,11 +977,18 @@ fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id { |
| 975 | 977 | .signed => @bitCast(@as(i64, @intCast(value))), |
| 976 | 978 | .unsigned => @as(u64, @intCast(value)), |
| 977 | 979 | }; |
| 978 | | assert(backing_bits == 64); |
| 979 | | return cg.constructComposite(result_ty_id, &.{ |
| 980 | | try cg.constInt(.u32, @as(u32, @truncate(value64))), |
| 981 | | try cg.constInt(.u32, @as(u32, @truncate(value64 << 32))), |
| 982 | | }); |
| 980 | const n_limbs = backing_bits / Module.big_int_bits; |
| 981 | const fill: u32 = if (signedness == .signed and value < 0) 0xFFFFFFFF else 0; |
| 982 | const scratch_top = cg.id_scratch.items.len; |
| 983 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 984 | const constituents = try cg.id_scratch.addManyAsSlice(gpa, n_limbs); |
| 985 | for (constituents, 0..) |*c, i| { |
| 986 | c.* = try cg.constInt( |
| 987 | .u32, |
| 988 | if (i < 2) @as(u32, @truncate(value64 >> @intCast(i * 32))) else fill, |
| 989 | ); |
| 990 | } |
| 991 | return cg.constructComposite(result_ty_id, constituents); |
| 983 | 992 | } |
| 984 | 993 | |
| 985 | 994 | const final_value: spec.LiteralContextDependentNumber = switch (target.os.tag) { |
| ... | ... | @@ -1014,6 +1023,35 @@ fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id { |
| 1014 | 1023 | return cg.constructCompositeSplat(ty, result_id); |
| 1015 | 1024 | } |
| 1016 | 1025 | |
| 1026 | fn constIntBig(cg: *CodeGen, ty: Type, val: Value) !Id { |
| 1027 | const gpa = cg.module.gpa; |
| 1028 | const zcu = cg.module.zcu; |
| 1029 | const int_info = ty.intInfo(zcu); |
| 1030 | const backing_bits, _ = cg.module.backingIntBits(int_info.bits); |
| 1031 | const n_limbs = backing_bits / Module.big_int_bits; |
| 1032 | const result_ty_id = try cg.resolveType(ty, .indirect); |
| 1033 | |
| 1034 | var bigint_space: Value.BigIntSpace = undefined; |
| 1035 | const bigint = val.toBigInt(&bigint_space, zcu); |
| 1036 | |
| 1037 | const limb_values = try gpa.alloc(u32, n_limbs); |
| 1038 | defer gpa.free(limb_values); |
| 1039 | |
| 1040 | const bytes = std.mem.sliceAsBytes(limb_values); |
| 1041 | bigint.writeTwosComplement(bytes, .little); |
| 1042 | if (builtin.cpu.arch.endian() == .big) { |
| 1043 | for (limb_values) |*limb| limb.* = @byteSwap(limb.*); |
| 1044 | } |
| 1045 | |
| 1046 | const scratch_top = cg.id_scratch.items.len; |
| 1047 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 1048 | const constituents = try cg.id_scratch.addManyAsSlice(gpa, n_limbs); |
| 1049 | for (constituents, 0..) |*c, i| { |
| 1050 | c.* = try cg.constInt(.u32, limb_values[i]); |
| 1051 | } |
| 1052 | return cg.constructComposite(result_ty_id, constituents); |
| 1053 | } |
| 1054 | |
| 1017 | 1055 | pub fn constructComposite(cg: *CodeGen, result_ty_id: Id, constituents: []const Id) !Id { |
| 1018 | 1056 | const gpa = cg.module.gpa; |
| 1019 | 1057 | const result_id = cg.module.allocId(); |
| ... | ... | @@ -1106,6 +1144,11 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id { |
| 1106 | 1144 | .false, .true => break :cache try cg.constBool(val.toBool(), repr), |
| 1107 | 1145 | }, |
| 1108 | 1146 | .int => { |
| 1147 | const int_info = ty.intInfo(zcu); |
| 1148 | _, const is_big_int = cg.module.backingIntBits(int_info.bits); |
| 1149 | if (is_big_int) { |
| 1150 | break :cache try cg.constIntBig(ty, val); |
| 1151 | } |
| 1109 | 1152 | if (ty.isSignedInt(zcu)) { |
| 1110 | 1153 | break :cache try cg.constInt(ty, val.toSignedInt(zcu)); |
| 1111 | 1154 | } else { |
| ... | ... | @@ -1385,15 +1428,32 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 1385 | 1428 | } |
| 1386 | 1429 | |
| 1387 | 1430 | if (oac.byte_offset == 0) { |
| 1388 | | // Allow changing the pointer type child only to restructure arrays. |
| 1389 | | // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T. |
| 1390 | | const result_ptr_id = cg.module.allocId(); |
| 1391 | | try cg.body.emit(gpa, .OpBitcast, .{ |
| 1392 | | .id_result_type = result_ty_id, |
| 1393 | | .id_result = result_ptr_id, |
| 1394 | | .operand = parent_ptr_id, |
| 1395 | | }); |
| 1396 | | return result_ptr_id; |
| 1431 | var depth: u32 = 0; |
| 1432 | var cur = parent_ptr_ty.childType(zcu); |
| 1433 | const dst_child = oac.new_ptr_ty.childType(zcu); |
| 1434 | while (cur.toIntern() != dst_child.toIntern()) { |
| 1435 | if (cur.zigTypeTag(zcu) == .array) { |
| 1436 | cur = cur.childType(zcu); |
| 1437 | depth += 1; |
| 1438 | } else break; |
| 1439 | } |
| 1440 | if (depth > 0 and cur.toIntern() == dst_child.toIntern()) { |
| 1441 | const scratch_top = cg.id_scratch.items.len; |
| 1442 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 1443 | const zero = try cg.constInt(.u32, 0); |
| 1444 | const ids = try cg.id_scratch.addManyAsSlice(gpa, depth); |
| 1445 | @memset(ids, zero); |
| 1446 | return cg.accessChainId(result_ty_id, parent_ptr_id, ids); |
| 1447 | } |
| 1448 | if (target.os.tag == .opencl) { |
| 1449 | const result_ptr_id = cg.module.allocId(); |
| 1450 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 1451 | .id_result_type = result_ty_id, |
| 1452 | .id_result = result_ptr_id, |
| 1453 | .operand = parent_ptr_id, |
| 1454 | }); |
| 1455 | return result_ptr_id; |
| 1456 | } |
| 1397 | 1457 | } |
| 1398 | 1458 | |
| 1399 | 1459 | return cg.fail("cannot perform pointer cast: '{f}' to '{f}'", .{ |
| ... | ... | @@ -1684,10 +1744,6 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 1684 | 1744 | return try cg.module.opaqueType("u0"); |
| 1685 | 1745 | } |
| 1686 | 1746 | const int_info = ty.intInfo(zcu); |
| 1687 | | const backing_bits, const big_int = cg.module.backingIntBits(int_info.bits); |
| 1688 | | if (big_int and backing_bits > 64) { |
| 1689 | | return cg.fail("integer width of {} bits is not yet supported on the SPIR-V backend", .{int_info.bits}); |
| 1690 | | } |
| 1691 | 1747 | return try cg.module.intType(int_info.signedness, int_info.bits); |
| 1692 | 1748 | }, |
| 1693 | 1749 | .@"enum" => return try cg.resolveType(ty.intTagType(zcu), repr), |
| ... | ... | @@ -2216,6 +2272,697 @@ const Temporary = struct { |
| 2216 | 2272 | } |
| 2217 | 2273 | }; |
| 2218 | 2274 | |
| 2275 | /// composite integers are represented as [N]u32 arrays |
| 2276 | const CompositeInt = struct { |
| 2277 | cg: *CodeGen, |
| 2278 | limbs: []Id, |
| 2279 | n_limbs: u16, |
| 2280 | info: ArithmeticTypeInfo, |
| 2281 | |
| 2282 | fn init(cg: *CodeGen, composite_id: Id, info: ArithmeticTypeInfo) !CompositeInt { |
| 2283 | const n_limbs: u16 = info.backing_bits / Module.big_int_bits; |
| 2284 | const gpa = cg.module.gpa; |
| 2285 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2286 | const limbs = try cg.id_scratch.addManyAsSlice(gpa, n_limbs); |
| 2287 | for (limbs, 0..) |*limb, i| { |
| 2288 | const result_id = cg.module.allocId(); |
| 2289 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2290 | .id_result_type = u32_ty_id, |
| 2291 | .id_result = result_id, |
| 2292 | .composite = composite_id, |
| 2293 | .indexes = &.{@as(u32, @intCast(i))}, |
| 2294 | }); |
| 2295 | limb.* = result_id; |
| 2296 | } |
| 2297 | return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info }; |
| 2298 | } |
| 2299 | |
| 2300 | fn fromLimbs(cg: *CodeGen, limbs: []Id, info: ArithmeticTypeInfo) CompositeInt { |
| 2301 | return .{ |
| 2302 | .cg = cg, |
| 2303 | .limbs = limbs, |
| 2304 | .n_limbs = @intCast(limbs.len), |
| 2305 | .info = info, |
| 2306 | }; |
| 2307 | } |
| 2308 | |
| 2309 | fn zero(cg: *CodeGen, info: ArithmeticTypeInfo) !CompositeInt { |
| 2310 | const n_limbs: u16 = info.backing_bits / Module.big_int_bits; |
| 2311 | const limbs = try cg.id_scratch.addManyAsSlice(cg.module.gpa, n_limbs); |
| 2312 | const zero_id = try cg.constInt(.u32, @as(u32, 0)); |
| 2313 | for (limbs) |*limb| limb.* = zero_id; |
| 2314 | return .{ .cg = cg, .limbs = limbs, .n_limbs = n_limbs, .info = info }; |
| 2315 | } |
| 2316 | |
| 2317 | fn materialize(ci: CompositeInt, ty: Type) !Id { |
| 2318 | const result_ty_id = try ci.cg.resolveType(ty, .indirect); |
| 2319 | return ci.cg.constructComposite(result_ty_id, ci.limbs); |
| 2320 | } |
| 2321 | |
| 2322 | fn limbBinOp(ci: CompositeInt, opcode: Opcode, lhs: Id, rhs: Id) !Id { |
| 2323 | const cg = ci.cg; |
| 2324 | const gpa = cg.module.gpa; |
| 2325 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2326 | const result_id = cg.module.allocId(); |
| 2327 | try cg.body.emitRaw(gpa, opcode, 4); |
| 2328 | cg.body.writeOperand(Id, u32_ty_id); |
| 2329 | cg.body.writeOperand(Id, result_id); |
| 2330 | cg.body.writeOperand(Id, lhs); |
| 2331 | cg.body.writeOperand(Id, rhs); |
| 2332 | return result_id; |
| 2333 | } |
| 2334 | |
| 2335 | fn limbUnOp(ci: CompositeInt, opcode: Opcode, operand: Id) !Id { |
| 2336 | const cg = ci.cg; |
| 2337 | const gpa = cg.module.gpa; |
| 2338 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2339 | const result_id = cg.module.allocId(); |
| 2340 | try cg.body.emitRaw(gpa, opcode, 3); |
| 2341 | cg.body.writeOperand(Id, u32_ty_id); |
| 2342 | cg.body.writeOperand(Id, result_id); |
| 2343 | cg.body.writeOperand(Id, operand); |
| 2344 | return result_id; |
| 2345 | } |
| 2346 | |
| 2347 | fn bitwiseOp(ci: CompositeInt, other: CompositeInt, opcode: Opcode) !CompositeInt { |
| 2348 | const cg = ci.cg; |
| 2349 | const gpa = cg.module.gpa; |
| 2350 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs); |
| 2351 | for (result_limbs, 0..) |*r, i| { |
| 2352 | r.* = try ci.limbBinOp(opcode, ci.limbs[i], other.limbs[i]); |
| 2353 | } |
| 2354 | return .fromLimbs(cg, result_limbs, ci.info); |
| 2355 | } |
| 2356 | |
| 2357 | fn bitwiseNot(ci: CompositeInt) !CompositeInt { |
| 2358 | const cg = ci.cg; |
| 2359 | const gpa = cg.module.gpa; |
| 2360 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs); |
| 2361 | for (result_limbs, 0..) |*r, i| { |
| 2362 | r.* = try ci.limbUnOp(.OpNot, ci.limbs[i]); |
| 2363 | } |
| 2364 | return .fromLimbs(cg, result_limbs, ci.info); |
| 2365 | } |
| 2366 | |
| 2367 | fn cmp(ci: CompositeInt, other: CompositeInt, op: std.math.CompareOperator) !Id { |
| 2368 | const cg = ci.cg; |
| 2369 | const gpa = cg.module.gpa; |
| 2370 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 2371 | |
| 2372 | switch (op) { |
| 2373 | .eq, .neq => { |
| 2374 | var result = blk: { |
| 2375 | const r = cg.module.allocId(); |
| 2376 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2377 | cg.body.writeOperand(Id, bool_ty_id); |
| 2378 | cg.body.writeOperand(Id, r); |
| 2379 | cg.body.writeOperand(Id, ci.limbs[0]); |
| 2380 | cg.body.writeOperand(Id, other.limbs[0]); |
| 2381 | break :blk r; |
| 2382 | }; |
| 2383 | for (1..ci.n_limbs) |i| { |
| 2384 | const limb_eq = cg.module.allocId(); |
| 2385 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2386 | cg.body.writeOperand(Id, bool_ty_id); |
| 2387 | cg.body.writeOperand(Id, limb_eq); |
| 2388 | cg.body.writeOperand(Id, ci.limbs[i]); |
| 2389 | cg.body.writeOperand(Id, other.limbs[i]); |
| 2390 | const combined = cg.module.allocId(); |
| 2391 | try cg.body.emitRaw(gpa, .OpLogicalAnd, 4); |
| 2392 | cg.body.writeOperand(Id, bool_ty_id); |
| 2393 | cg.body.writeOperand(Id, combined); |
| 2394 | cg.body.writeOperand(Id, result); |
| 2395 | cg.body.writeOperand(Id, limb_eq); |
| 2396 | result = combined; |
| 2397 | } |
| 2398 | if (op == .neq) { |
| 2399 | const negated = cg.module.allocId(); |
| 2400 | try cg.body.emitRaw(gpa, .OpLogicalNot, 3); |
| 2401 | cg.body.writeOperand(Id, bool_ty_id); |
| 2402 | cg.body.writeOperand(Id, negated); |
| 2403 | cg.body.writeOperand(Id, result); |
| 2404 | result = negated; |
| 2405 | } |
| 2406 | return result; |
| 2407 | }, |
| 2408 | .lt, .lte, .gt, .gte => { |
| 2409 | const is_lt = (op == .lt or op == .lte); |
| 2410 | const is_strict = (op == .lt or op == .gt); |
| 2411 | var result = try cg.constBool(!is_strict, .direct); |
| 2412 | |
| 2413 | for (0..ci.n_limbs) |i| { |
| 2414 | const l = ci.limbs[i]; |
| 2415 | const r = other.limbs[i]; |
| 2416 | const limb_ne = cg.module.allocId(); |
| 2417 | try cg.body.emitRaw(gpa, .OpINotEqual, 4); |
| 2418 | cg.body.writeOperand(Id, bool_ty_id); |
| 2419 | cg.body.writeOperand(Id, limb_ne); |
| 2420 | cg.body.writeOperand(Id, l); |
| 2421 | cg.body.writeOperand(Id, r); |
| 2422 | |
| 2423 | const is_top = (i == ci.n_limbs - 1); |
| 2424 | const use_signed = is_top and ci.info.signedness == .signed; |
| 2425 | var cmp_l = l; |
| 2426 | var cmp_r = r; |
| 2427 | if (use_signed) { |
| 2428 | const i32_ty_id = try cg.resolveType(.i32, .direct); |
| 2429 | const sl = cg.module.allocId(); |
| 2430 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 2431 | .id_result_type = i32_ty_id, |
| 2432 | .id_result = sl, |
| 2433 | .operand = l, |
| 2434 | }); |
| 2435 | const sr = cg.module.allocId(); |
| 2436 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 2437 | .id_result_type = i32_ty_id, |
| 2438 | .id_result = sr, |
| 2439 | .operand = r, |
| 2440 | }); |
| 2441 | cmp_l = sl; |
| 2442 | cmp_r = sr; |
| 2443 | } |
| 2444 | |
| 2445 | const cmp_opcode: Opcode = if (is_lt) |
| 2446 | (if (use_signed) .OpSLessThan else .OpULessThan) |
| 2447 | else |
| 2448 | (if (use_signed) .OpSGreaterThan else .OpUGreaterThan); |
| 2449 | |
| 2450 | const limb_cmp = cg.module.allocId(); |
| 2451 | try cg.body.emitRaw(gpa, cmp_opcode, 4); |
| 2452 | cg.body.writeOperand(Id, bool_ty_id); |
| 2453 | cg.body.writeOperand(Id, limb_cmp); |
| 2454 | cg.body.writeOperand(Id, cmp_l); |
| 2455 | cg.body.writeOperand(Id, cmp_r); |
| 2456 | |
| 2457 | const selected = cg.module.allocId(); |
| 2458 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2459 | .id_result_type = bool_ty_id, |
| 2460 | .id_result = selected, |
| 2461 | .condition = limb_ne, |
| 2462 | .object_1 = limb_cmp, |
| 2463 | .object_2 = result, |
| 2464 | }); |
| 2465 | result = selected; |
| 2466 | } |
| 2467 | return result; |
| 2468 | }, |
| 2469 | } |
| 2470 | } |
| 2471 | |
| 2472 | fn addSub(ci: CompositeInt, other: CompositeInt, comptime is_add: bool) !CompositeInt { |
| 2473 | const cg = ci.cg; |
| 2474 | const gpa = cg.module.gpa; |
| 2475 | const pt = cg.pt; |
| 2476 | const zcu = cg.module.zcu; |
| 2477 | const ip = &zcu.intern_pool; |
| 2478 | const comp = zcu.comp; |
| 2479 | const io = comp.io; |
| 2480 | |
| 2481 | const u32_zig = try pt.intType(.unsigned, 32); |
| 2482 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2483 | const carry_struct_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{ |
| 2484 | .types = &.{ u32_zig.toIntern(), u32_zig.toIntern() }, |
| 2485 | .values = &.{ .none, .none }, |
| 2486 | })); |
| 2487 | const carry_struct_ty_id = try cg.resolveType(carry_struct_ty, .direct); |
| 2488 | |
| 2489 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs); |
| 2490 | var carry_id = try cg.constInt(.u32, @as(u32, 0)); |
| 2491 | |
| 2492 | const opcode: Opcode = if (is_add) .OpIAddCarry else .OpISubBorrow; |
| 2493 | |
| 2494 | for (0..ci.n_limbs) |i| { |
| 2495 | const op1 = cg.module.allocId(); |
| 2496 | try cg.body.emitRaw(gpa, opcode, 4); |
| 2497 | cg.body.writeOperand(Id, carry_struct_ty_id); |
| 2498 | cg.body.writeOperand(Id, op1); |
| 2499 | cg.body.writeOperand(Id, ci.limbs[i]); |
| 2500 | cg.body.writeOperand(Id, other.limbs[i]); |
| 2501 | |
| 2502 | const sum1 = cg.module.allocId(); |
| 2503 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2504 | .id_result_type = u32_ty_id, |
| 2505 | .id_result = sum1, |
| 2506 | .composite = op1, |
| 2507 | .indexes = &.{0}, |
| 2508 | }); |
| 2509 | const carry1 = cg.module.allocId(); |
| 2510 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2511 | .id_result_type = u32_ty_id, |
| 2512 | .id_result = carry1, |
| 2513 | .composite = op1, |
| 2514 | .indexes = &.{1}, |
| 2515 | }); |
| 2516 | |
| 2517 | const op2 = cg.module.allocId(); |
| 2518 | try cg.body.emitRaw(gpa, opcode, 4); |
| 2519 | cg.body.writeOperand(Id, carry_struct_ty_id); |
| 2520 | cg.body.writeOperand(Id, op2); |
| 2521 | cg.body.writeOperand(Id, sum1); |
| 2522 | cg.body.writeOperand(Id, carry_id); |
| 2523 | |
| 2524 | result_limbs[i] = cg.module.allocId(); |
| 2525 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2526 | .id_result_type = u32_ty_id, |
| 2527 | .id_result = result_limbs[i], |
| 2528 | .composite = op2, |
| 2529 | .indexes = &.{0}, |
| 2530 | }); |
| 2531 | const carry2 = cg.module.allocId(); |
| 2532 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2533 | .id_result_type = u32_ty_id, |
| 2534 | .id_result = carry2, |
| 2535 | .composite = op2, |
| 2536 | .indexes = &.{1}, |
| 2537 | }); |
| 2538 | |
| 2539 | carry_id = try ci.limbBinOp(.OpBitwiseOr, carry1, carry2); |
| 2540 | } |
| 2541 | |
| 2542 | return .fromLimbs(cg, result_limbs, ci.info); |
| 2543 | } |
| 2544 | |
| 2545 | fn shl(ci: CompositeInt, shift_amt_id: Id) !CompositeInt { |
| 2546 | const cg = ci.cg; |
| 2547 | const gpa = cg.module.gpa; |
| 2548 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2549 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 2550 | const zero_id = try cg.constInt(.u32, @as(u32, 0)); |
| 2551 | const five_id = try cg.constInt(.u32, @as(u32, 5)); |
| 2552 | const thirty_one_id = try cg.constInt(.u32, @as(u32, 31)); |
| 2553 | const thirty_two_id = try cg.constInt(.u32, @as(u32, 32)); |
| 2554 | |
| 2555 | const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, five_id); |
| 2556 | const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, thirty_one_id); |
| 2557 | const comp_frac = try ci.limbBinOp(.OpISub, thirty_two_id, frac); |
| 2558 | const frac_is_zero = blk: { |
| 2559 | const r = cg.module.allocId(); |
| 2560 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2561 | cg.body.writeOperand(Id, bool_ty_id); |
| 2562 | cg.body.writeOperand(Id, r); |
| 2563 | cg.body.writeOperand(Id, frac); |
| 2564 | cg.body.writeOperand(Id, zero_id); |
| 2565 | break :blk r; |
| 2566 | }; |
| 2567 | |
| 2568 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs); |
| 2569 | |
| 2570 | for (0..ci.n_limbs) |i| { |
| 2571 | const i_id = try cg.constInt(.u32, @as(u32, @intCast(i))); |
| 2572 | var main_val = zero_id; |
| 2573 | var carry_val = zero_id; |
| 2574 | |
| 2575 | for (0..ci.n_limbs) |j| { |
| 2576 | const j_id = try cg.constInt(.u32, @as(u32, @intCast(j))); |
| 2577 | const j_plus_whole = try ci.limbBinOp(.OpIAdd, j_id, whole); |
| 2578 | |
| 2579 | const is_main = blk: { |
| 2580 | const r = cg.module.allocId(); |
| 2581 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2582 | cg.body.writeOperand(Id, bool_ty_id); |
| 2583 | cg.body.writeOperand(Id, r); |
| 2584 | cg.body.writeOperand(Id, j_plus_whole); |
| 2585 | cg.body.writeOperand(Id, i_id); |
| 2586 | break :blk r; |
| 2587 | }; |
| 2588 | const shifted = try ci.limbBinOp(.OpShiftLeftLogical, ci.limbs[j], frac); |
| 2589 | main_val = blk: { |
| 2590 | const r = cg.module.allocId(); |
| 2591 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2592 | .id_result_type = u32_ty_id, |
| 2593 | .id_result = r, |
| 2594 | .condition = is_main, |
| 2595 | .object_1 = shifted, |
| 2596 | .object_2 = main_val, |
| 2597 | }); |
| 2598 | break :blk r; |
| 2599 | }; |
| 2600 | |
| 2601 | const one_id = try cg.constInt(.u32, @as(u32, 1)); |
| 2602 | const j_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, j_plus_whole, one_id); |
| 2603 | const is_carry = blk: { |
| 2604 | const r = cg.module.allocId(); |
| 2605 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2606 | cg.body.writeOperand(Id, bool_ty_id); |
| 2607 | cg.body.writeOperand(Id, r); |
| 2608 | cg.body.writeOperand(Id, j_plus_whole_plus_1); |
| 2609 | cg.body.writeOperand(Id, i_id); |
| 2610 | break :blk r; |
| 2611 | }; |
| 2612 | const carry_shifted = try ci.limbBinOp(.OpShiftRightLogical, ci.limbs[j], comp_frac); |
| 2613 | const guarded_carry = blk: { |
| 2614 | const r = cg.module.allocId(); |
| 2615 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2616 | .id_result_type = u32_ty_id, |
| 2617 | .id_result = r, |
| 2618 | .condition = frac_is_zero, |
| 2619 | .object_1 = zero_id, |
| 2620 | .object_2 = carry_shifted, |
| 2621 | }); |
| 2622 | break :blk r; |
| 2623 | }; |
| 2624 | carry_val = blk: { |
| 2625 | const r = cg.module.allocId(); |
| 2626 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2627 | .id_result_type = u32_ty_id, |
| 2628 | .id_result = r, |
| 2629 | .condition = is_carry, |
| 2630 | .object_1 = guarded_carry, |
| 2631 | .object_2 = carry_val, |
| 2632 | }); |
| 2633 | break :blk r; |
| 2634 | }; |
| 2635 | } |
| 2636 | |
| 2637 | result_limbs[i] = try ci.limbBinOp(.OpBitwiseOr, main_val, carry_val); |
| 2638 | } |
| 2639 | |
| 2640 | return .fromLimbs(cg, result_limbs, ci.info); |
| 2641 | } |
| 2642 | |
| 2643 | fn shr(ci: CompositeInt, shift_amt_id: Id, comptime is_arithmetic: bool) !CompositeInt { |
| 2644 | const cg = ci.cg; |
| 2645 | const gpa = cg.module.gpa; |
| 2646 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2647 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 2648 | const zero_id = try cg.constInt(.u32, @as(u32, 0)); |
| 2649 | const five_id = try cg.constInt(.u32, @as(u32, 5)); |
| 2650 | const thirty_one_id = try cg.constInt(.u32, @as(u32, 31)); |
| 2651 | const thirty_two_id = try cg.constInt(.u32, @as(u32, 32)); |
| 2652 | |
| 2653 | const whole = try ci.limbBinOp(.OpShiftRightLogical, shift_amt_id, five_id); |
| 2654 | const frac = try ci.limbBinOp(.OpBitwiseAnd, shift_amt_id, thirty_one_id); |
| 2655 | const comp_frac = try ci.limbBinOp(.OpISub, thirty_two_id, frac); |
| 2656 | const frac_is_zero = blk: { |
| 2657 | const r = cg.module.allocId(); |
| 2658 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2659 | cg.body.writeOperand(Id, bool_ty_id); |
| 2660 | cg.body.writeOperand(Id, r); |
| 2661 | cg.body.writeOperand(Id, frac); |
| 2662 | cg.body.writeOperand(Id, zero_id); |
| 2663 | break :blk r; |
| 2664 | }; |
| 2665 | |
| 2666 | const fill_id = if (is_arithmetic) blk: { |
| 2667 | const i32_ty_id = try cg.resolveType(.i32, .direct); |
| 2668 | const msb_signed = cg.module.allocId(); |
| 2669 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 2670 | .id_result_type = i32_ty_id, |
| 2671 | .id_result = msb_signed, |
| 2672 | .operand = ci.limbs[ci.n_limbs - 1], |
| 2673 | }); |
| 2674 | const shift31 = try cg.constInt(.i32, @as(i32, 31)); |
| 2675 | const sign_ext = cg.module.allocId(); |
| 2676 | try cg.body.emitRaw(gpa, .OpShiftRightArithmetic, 4); |
| 2677 | cg.body.writeOperand(Id, i32_ty_id); |
| 2678 | cg.body.writeOperand(Id, sign_ext); |
| 2679 | cg.body.writeOperand(Id, msb_signed); |
| 2680 | cg.body.writeOperand(Id, shift31); |
| 2681 | const back = cg.module.allocId(); |
| 2682 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 2683 | .id_result_type = u32_ty_id, |
| 2684 | .id_result = back, |
| 2685 | .operand = sign_ext, |
| 2686 | }); |
| 2687 | break :blk back; |
| 2688 | } else zero_id; |
| 2689 | |
| 2690 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs); |
| 2691 | |
| 2692 | const arith_carry_init = if (is_arithmetic) blk: { |
| 2693 | const shifted_fill = try ci.limbBinOp(.OpShiftLeftLogical, fill_id, comp_frac); |
| 2694 | const guarded = cg.module.allocId(); |
| 2695 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2696 | .id_result_type = u32_ty_id, |
| 2697 | .id_result = guarded, |
| 2698 | .condition = frac_is_zero, |
| 2699 | .object_1 = zero_id, |
| 2700 | .object_2 = shifted_fill, |
| 2701 | }); |
| 2702 | break :blk guarded; |
| 2703 | } else zero_id; |
| 2704 | |
| 2705 | for (0..ci.n_limbs) |i| { |
| 2706 | const i_id = try cg.constInt(.u32, @as(u32, @intCast(i))); |
| 2707 | var main_val = fill_id; |
| 2708 | var carry_val = arith_carry_init; |
| 2709 | |
| 2710 | for (0..ci.n_limbs) |j| { |
| 2711 | const j_id = try cg.constInt(.u32, @as(u32, @intCast(j))); |
| 2712 | const i_plus_whole = try ci.limbBinOp(.OpIAdd, i_id, whole); |
| 2713 | const is_main = blk: { |
| 2714 | const r = cg.module.allocId(); |
| 2715 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2716 | cg.body.writeOperand(Id, bool_ty_id); |
| 2717 | cg.body.writeOperand(Id, r); |
| 2718 | cg.body.writeOperand(Id, j_id); |
| 2719 | cg.body.writeOperand(Id, i_plus_whole); |
| 2720 | break :blk r; |
| 2721 | }; |
| 2722 | const shifted = try ci.limbBinOp(.OpShiftRightLogical, ci.limbs[j], frac); |
| 2723 | main_val = blk: { |
| 2724 | const r = cg.module.allocId(); |
| 2725 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2726 | .id_result_type = u32_ty_id, |
| 2727 | .id_result = r, |
| 2728 | .condition = is_main, |
| 2729 | .object_1 = shifted, |
| 2730 | .object_2 = main_val, |
| 2731 | }); |
| 2732 | break :blk r; |
| 2733 | }; |
| 2734 | |
| 2735 | const one_id = try cg.constInt(.u32, @as(u32, 1)); |
| 2736 | const i_plus_whole_plus_1 = try ci.limbBinOp(.OpIAdd, i_plus_whole, one_id); |
| 2737 | const is_carry = blk: { |
| 2738 | const r = cg.module.allocId(); |
| 2739 | try cg.body.emitRaw(gpa, .OpIEqual, 4); |
| 2740 | cg.body.writeOperand(Id, bool_ty_id); |
| 2741 | cg.body.writeOperand(Id, r); |
| 2742 | cg.body.writeOperand(Id, j_id); |
| 2743 | cg.body.writeOperand(Id, i_plus_whole_plus_1); |
| 2744 | break :blk r; |
| 2745 | }; |
| 2746 | const carry_shifted = try ci.limbBinOp(.OpShiftLeftLogical, ci.limbs[j], comp_frac); |
| 2747 | const guarded_carry = blk: { |
| 2748 | const r = cg.module.allocId(); |
| 2749 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2750 | .id_result_type = u32_ty_id, |
| 2751 | .id_result = r, |
| 2752 | .condition = frac_is_zero, |
| 2753 | .object_1 = zero_id, |
| 2754 | .object_2 = carry_shifted, |
| 2755 | }); |
| 2756 | break :blk r; |
| 2757 | }; |
| 2758 | carry_val = blk: { |
| 2759 | const r = cg.module.allocId(); |
| 2760 | try cg.body.emit(gpa, .OpSelect, .{ |
| 2761 | .id_result_type = u32_ty_id, |
| 2762 | .id_result = r, |
| 2763 | .condition = is_carry, |
| 2764 | .object_1 = guarded_carry, |
| 2765 | .object_2 = carry_val, |
| 2766 | }); |
| 2767 | break :blk r; |
| 2768 | }; |
| 2769 | } |
| 2770 | |
| 2771 | result_limbs[i] = try ci.limbBinOp(.OpBitwiseOr, main_val, carry_val); |
| 2772 | } |
| 2773 | |
| 2774 | return .fromLimbs(cg, result_limbs, ci.info); |
| 2775 | } |
| 2776 | |
| 2777 | fn mul(ci: CompositeInt, other: CompositeInt, comptime wide: bool) ![]Id { |
| 2778 | const cg = ci.cg; |
| 2779 | const gpa = cg.module.gpa; |
| 2780 | const pt = cg.pt; |
| 2781 | const zcu = cg.module.zcu; |
| 2782 | const ip = &zcu.intern_pool; |
| 2783 | const comp = zcu.comp; |
| 2784 | const io = comp.io; |
| 2785 | const target = zcu.getTarget(); |
| 2786 | |
| 2787 | const n: usize = ci.n_limbs; |
| 2788 | const total: usize = if (wide) 2 * n else n; |
| 2789 | const u32_zig = try pt.intType(.unsigned, 32); |
| 2790 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2791 | |
| 2792 | const pair_struct_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{ |
| 2793 | .types = &.{ u32_zig.toIntern(), u32_zig.toIntern() }, |
| 2794 | .values = &.{ .none, .none }, |
| 2795 | })); |
| 2796 | const pair_struct_ty_id = try cg.resolveType(pair_struct_ty, .direct); |
| 2797 | |
| 2798 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, total); |
| 2799 | const zero_id = try cg.constInt(.u32, @as(u32, 0)); |
| 2800 | for (result_limbs) |*r| r.* = zero_id; |
| 2801 | |
| 2802 | for (0..n) |i| { |
| 2803 | var carry_id = zero_id; |
| 2804 | for (0..n) |j| { |
| 2805 | const k = i + j; |
| 2806 | if (k >= total) break; |
| 2807 | |
| 2808 | var lo: Id = undefined; |
| 2809 | var hi: Id = undefined; |
| 2810 | switch (target.os.tag) { |
| 2811 | .opencl => { |
| 2812 | lo = cg.module.allocId(); |
| 2813 | try cg.body.emitRaw(gpa, .OpIMul, 4); |
| 2814 | cg.body.writeOperand(Id, u32_ty_id); |
| 2815 | cg.body.writeOperand(Id, lo); |
| 2816 | cg.body.writeOperand(Id, ci.limbs[i]); |
| 2817 | cg.body.writeOperand(Id, other.limbs[j]); |
| 2818 | |
| 2819 | const set = try cg.importExtendedSet(); |
| 2820 | hi = cg.module.allocId(); |
| 2821 | try cg.body.emit(gpa, .OpExtInst, .{ |
| 2822 | .id_result_type = u32_ty_id, |
| 2823 | .id_result = hi, |
| 2824 | .set = set, |
| 2825 | .instruction = .{ .inst = @intFromEnum(spec.OpenClOpcode.u_mul_hi) }, |
| 2826 | .id_ref_4 = &.{ ci.limbs[i], other.limbs[j] }, |
| 2827 | }); |
| 2828 | }, |
| 2829 | else => { |
| 2830 | const mul_result = cg.module.allocId(); |
| 2831 | try cg.body.emitRaw(gpa, .OpUMulExtended, 4); |
| 2832 | cg.body.writeOperand(Id, pair_struct_ty_id); |
| 2833 | cg.body.writeOperand(Id, mul_result); |
| 2834 | cg.body.writeOperand(Id, ci.limbs[i]); |
| 2835 | cg.body.writeOperand(Id, other.limbs[j]); |
| 2836 | |
| 2837 | lo = cg.module.allocId(); |
| 2838 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2839 | .id_result_type = u32_ty_id, |
| 2840 | .id_result = lo, |
| 2841 | .composite = mul_result, |
| 2842 | .indexes = &.{0}, |
| 2843 | }); |
| 2844 | hi = cg.module.allocId(); |
| 2845 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2846 | .id_result_type = u32_ty_id, |
| 2847 | .id_result = hi, |
| 2848 | .composite = mul_result, |
| 2849 | .indexes = &.{1}, |
| 2850 | }); |
| 2851 | }, |
| 2852 | } |
| 2853 | |
| 2854 | const add1 = cg.module.allocId(); |
| 2855 | try cg.body.emitRaw(gpa, .OpIAddCarry, 4); |
| 2856 | cg.body.writeOperand(Id, pair_struct_ty_id); |
| 2857 | cg.body.writeOperand(Id, add1); |
| 2858 | cg.body.writeOperand(Id, result_limbs[k]); |
| 2859 | cg.body.writeOperand(Id, lo); |
| 2860 | |
| 2861 | const sum1 = cg.module.allocId(); |
| 2862 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2863 | .id_result_type = u32_ty_id, |
| 2864 | .id_result = sum1, |
| 2865 | .composite = add1, |
| 2866 | .indexes = &.{0}, |
| 2867 | }); |
| 2868 | const c1 = cg.module.allocId(); |
| 2869 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2870 | .id_result_type = u32_ty_id, |
| 2871 | .id_result = c1, |
| 2872 | .composite = add1, |
| 2873 | .indexes = &.{1}, |
| 2874 | }); |
| 2875 | |
| 2876 | const add2 = cg.module.allocId(); |
| 2877 | try cg.body.emitRaw(gpa, .OpIAddCarry, 4); |
| 2878 | cg.body.writeOperand(Id, pair_struct_ty_id); |
| 2879 | cg.body.writeOperand(Id, add2); |
| 2880 | cg.body.writeOperand(Id, sum1); |
| 2881 | cg.body.writeOperand(Id, carry_id); |
| 2882 | |
| 2883 | result_limbs[k] = cg.module.allocId(); |
| 2884 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2885 | .id_result_type = u32_ty_id, |
| 2886 | .id_result = result_limbs[k], |
| 2887 | .composite = add2, |
| 2888 | .indexes = &.{0}, |
| 2889 | }); |
| 2890 | const c2 = cg.module.allocId(); |
| 2891 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 2892 | .id_result_type = u32_ty_id, |
| 2893 | .id_result = c2, |
| 2894 | .composite = add2, |
| 2895 | .indexes = &.{1}, |
| 2896 | }); |
| 2897 | |
| 2898 | const hi_plus_c1 = try ci.limbBinOp(.OpIAdd, hi, c1); |
| 2899 | carry_id = try ci.limbBinOp(.OpIAdd, hi_plus_c1, c2); |
| 2900 | } |
| 2901 | if (wide and i + n < 2 * n) { |
| 2902 | result_limbs[i + n] = try ci.limbBinOp(.OpIAdd, result_limbs[i + n], carry_id); |
| 2903 | } |
| 2904 | } |
| 2905 | |
| 2906 | return result_limbs; |
| 2907 | } |
| 2908 | |
| 2909 | fn normalize(ci: CompositeInt) !CompositeInt { |
| 2910 | if (ci.info.bits == ci.info.backing_bits) return ci; |
| 2911 | const cg = ci.cg; |
| 2912 | const gpa = cg.module.gpa; |
| 2913 | const top_bits: u16 = ci.info.bits % Module.big_int_bits; |
| 2914 | assert(top_bits != 0); |
| 2915 | |
| 2916 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, ci.n_limbs); |
| 2917 | for (0..ci.n_limbs - 1) |i| { |
| 2918 | result_limbs[i] = ci.limbs[i]; |
| 2919 | } |
| 2920 | |
| 2921 | const top_limb = ci.limbs[ci.n_limbs - 1]; |
| 2922 | switch (ci.info.signedness) { |
| 2923 | .unsigned => { |
| 2924 | const mask_val: u32 = (@as(u32, 1) << @as(u5, @intCast(top_bits))) - 1; |
| 2925 | const mask_id = try cg.constInt(.u32, mask_val); |
| 2926 | result_limbs[ci.n_limbs - 1] = try ci.limbBinOp(.OpBitwiseAnd, top_limb, mask_id); |
| 2927 | }, |
| 2928 | .signed => { |
| 2929 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 2930 | const i32_ty_id = try cg.resolveType(.i32, .direct); |
| 2931 | const shift_amt: u32 = 32 - top_bits; |
| 2932 | const shift_id = try cg.constInt(.u32, shift_amt); |
| 2933 | |
| 2934 | const as_signed = cg.module.allocId(); |
| 2935 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 2936 | .id_result_type = i32_ty_id, |
| 2937 | .id_result = as_signed, |
| 2938 | .operand = top_limb, |
| 2939 | }); |
| 2940 | const shifted_left = cg.module.allocId(); |
| 2941 | try cg.body.emitRaw(gpa, .OpShiftLeftLogical, 4); |
| 2942 | cg.body.writeOperand(Id, i32_ty_id); |
| 2943 | cg.body.writeOperand(Id, shifted_left); |
| 2944 | cg.body.writeOperand(Id, as_signed); |
| 2945 | cg.body.writeOperand(Id, shift_id); |
| 2946 | const shifted_right = cg.module.allocId(); |
| 2947 | try cg.body.emitRaw(gpa, .OpShiftRightArithmetic, 4); |
| 2948 | cg.body.writeOperand(Id, i32_ty_id); |
| 2949 | cg.body.writeOperand(Id, shifted_right); |
| 2950 | cg.body.writeOperand(Id, shifted_left); |
| 2951 | cg.body.writeOperand(Id, shift_id); |
| 2952 | const back = cg.module.allocId(); |
| 2953 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 2954 | .id_result_type = u32_ty_id, |
| 2955 | .id_result = back, |
| 2956 | .operand = shifted_right, |
| 2957 | }); |
| 2958 | result_limbs[ci.n_limbs - 1] = back; |
| 2959 | }, |
| 2960 | } |
| 2961 | |
| 2962 | return .fromLimbs(cg, result_limbs, ci.info); |
| 2963 | } |
| 2964 | }; |
| 2965 | |
| 2219 | 2966 | /// Initialize a `Temporary` from an AIR value. |
| 2220 | 2967 | fn temporary(cg: *CodeGen, inst: Air.Inst.Ref) !Temporary { |
| 2221 | 2968 | return .{ |
| ... | ... | @@ -3234,7 +3981,21 @@ fn airBitwiseOp(cg: *CodeGen, inst: Air.Inst.Index, op: BitwiseOp) !?Id { |
| 3234 | 3981 | .xor => .OpBitwiseXor, |
| 3235 | 3982 | }, |
| 3236 | 3983 | .float => unreachable, |
| 3237 | | .composite_integer => unreachable, // TODO |
| 3984 | .composite_integer => { |
| 3985 | const spv_opcode: Opcode = switch (op) { |
| 3986 | .bit_and => .OpBitwiseAnd, |
| 3987 | .bit_or => .OpBitwiseOr, |
| 3988 | .xor => .OpBitwiseXor, |
| 3989 | }; |
| 3990 | const lhs_id = try lhs.materialize(cg); |
| 3991 | const rhs_id = try rhs.materialize(cg); |
| 3992 | const scratch_top = cg.id_scratch.items.len; |
| 3993 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 3994 | const ci_lhs = try CompositeInt.init(cg, lhs_id, info); |
| 3995 | const ci_rhs = try CompositeInt.init(cg, rhs_id, info); |
| 3996 | const ci_result = try ci_lhs.bitwiseOp(ci_rhs, spv_opcode); |
| 3997 | return try ci_result.materialize(lhs.ty); |
| 3998 | }, |
| 3238 | 3999 | }; |
| 3239 | 4000 | |
| 3240 | 4001 | const result = try cg.buildBinary(opcode, lhs, rhs); |
| ... | ... | @@ -3256,7 +4017,39 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode |
| 3256 | 4017 | |
| 3257 | 4018 | const info = cg.arithmeticTypeInfo(result_ty); |
| 3258 | 4019 | switch (info.class) { |
| 3259 | | .composite_integer => return cg.todo("shift ops for composite integers", .{}), |
| 4020 | .composite_integer => { |
| 4021 | const shift_info = cg.arithmeticTypeInfo(shift.ty); |
| 4022 | const shift_amt_id = switch (shift_info.class) { |
| 4023 | .composite_integer => blk: { |
| 4024 | const shift_id = try shift.materialize(cg); |
| 4025 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 4026 | const result_id = cg.module.allocId(); |
| 4027 | try cg.body.emit(cg.module.gpa, .OpCompositeExtract, .{ |
| 4028 | .id_result_type = u32_ty_id, |
| 4029 | .id_result = result_id, |
| 4030 | .composite = shift_id, |
| 4031 | .indexes = &.{@as(u32, 0)}, |
| 4032 | }); |
| 4033 | break :blk result_id; |
| 4034 | }, |
| 4035 | else => blk: { |
| 4036 | const converted = try cg.buildConvert(.u32, shift); |
| 4037 | break :blk try converted.materialize(cg); |
| 4038 | }, |
| 4039 | }; |
| 4040 | const base_id = try base.materialize(cg); |
| 4041 | const scratch_top = cg.id_scratch.items.len; |
| 4042 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 4043 | const ci = try CompositeInt.init(cg, base_id, info); |
| 4044 | const ci_result = if (unsigned == .OpShiftLeftLogical) |
| 4045 | try ci.shl(shift_amt_id) |
| 4046 | else switch (info.signedness) { |
| 4047 | .unsigned => try ci.shr(shift_amt_id, false), |
| 4048 | .signed => try ci.shr(shift_amt_id, true), |
| 4049 | }; |
| 4050 | const normalized = try ci_result.normalize(); |
| 4051 | return try normalized.materialize(result_ty); |
| 4052 | }, |
| 3260 | 4053 | .integer, .strange_integer => {}, |
| 3261 | 4054 | .float, .bool => unreachable, |
| 3262 | 4055 | } |
| ... | ... | @@ -3380,7 +4173,16 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar |
| 3380 | 4173 | const zcu = cg.module.zcu; |
| 3381 | 4174 | const ty = value.ty; |
| 3382 | 4175 | switch (info.class) { |
| 3383 | | .composite_integer, .integer, .bool, .float => return value, |
| 4176 | .integer, .bool, .float => return value, |
| 4177 | .composite_integer => { |
| 4178 | if (info.bits == info.backing_bits) return value; |
| 4179 | const val_id = try value.materialize(cg); |
| 4180 | const scratch_top = cg.id_scratch.items.len; |
| 4181 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 4182 | const ci = try CompositeInt.init(cg, val_id, info); |
| 4183 | const normalized = try ci.normalize(); |
| 4184 | return .init(ty, try normalized.materialize(ty)); |
| 4185 | }, |
| 3384 | 4186 | .strange_integer => switch (info.signedness) { |
| 3385 | 4187 | .unsigned => { |
| 3386 | 4188 | const mask_value = @as(u64, std.math.maxInt(u64)) >> @as(u6, @intCast(64 - info.bits)); |
| ... | ... | @@ -3484,7 +4286,22 @@ fn airArithOp( |
| 3484 | 4286 | const rhs = try cg.temporary(bin_op.rhs); |
| 3485 | 4287 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| 3486 | 4288 | const result = switch (info.class) { |
| 3487 | | .composite_integer => return cg.todo("arith op for composite integers", .{}), |
| 4289 | .composite_integer => res: { |
| 4290 | const lhs_id = try lhs.materialize(cg); |
| 4291 | const rhs_id = try rhs.materialize(cg); |
| 4292 | const scratch_top = cg.id_scratch.items.len; |
| 4293 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 4294 | const ci_lhs = try CompositeInt.init(cg, lhs_id, info); |
| 4295 | const ci_rhs = try CompositeInt.init(cg, rhs_id, info); |
| 4296 | const ci_result = switch (uop) { |
| 4297 | .OpIAdd => try ci_lhs.addSub(ci_rhs, true), |
| 4298 | .OpISub => try ci_lhs.addSub(ci_rhs, false), |
| 4299 | .OpIMul => CompositeInt.fromLimbs(cg, try ci_lhs.mul(ci_rhs, false), info), |
| 4300 | else => return cg.todo("arith op for composite integers", .{}), |
| 4301 | }; |
| 4302 | const normalized = try ci_result.normalize(); |
| 4303 | break :res Temporary.init(lhs.ty, try normalized.materialize(lhs.ty)); |
| 4304 | }, |
| 3488 | 4305 | .integer, .strange_integer => res: { |
| 3489 | 4306 | const raw = switch (info.signedness) { |
| 3490 | 4307 | .signed => try cg.buildBinary(sop, lhs, rhs), |
| ... | ... | @@ -3514,18 +4331,50 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary { |
| 3514 | 4331 | switch (operand_info.class) { |
| 3515 | 4332 | .float => return try cg.buildUnary(.f_abs, value), |
| 3516 | 4333 | .integer, .strange_integer => { |
| 3517 | | const abs_value = try cg.buildUnary(.i_abs, value); |
| 4334 | var abs_value = try cg.buildUnary(.i_abs, value); |
| 3518 | 4335 | switch (target.os.tag) { |
| 3519 | 4336 | .vulkan, .opengl => { |
| 3520 | 4337 | if (value.ty.intInfo(zcu).signedness == .signed) { |
| 3521 | | return cg.todo("perform bitcast after @abs", .{}); |
| 4338 | const abs_id = try abs_value.materialize(cg); |
| 4339 | const dst_ty_id = try cg.resolveType(result_ty, .direct); |
| 4340 | const cast_id = cg.module.allocId(); |
| 4341 | try cg.body.emit(cg.module.gpa, .OpBitcast, .{ |
| 4342 | .id_result_type = dst_ty_id, |
| 4343 | .id_result = cast_id, |
| 4344 | .operand = abs_id, |
| 4345 | }); |
| 4346 | abs_value = .init(result_ty, cast_id); |
| 3522 | 4347 | } |
| 3523 | 4348 | }, |
| 3524 | 4349 | else => {}, |
| 3525 | 4350 | } |
| 3526 | 4351 | return try cg.normalize(abs_value, cg.arithmeticTypeInfo(result_ty)); |
| 3527 | 4352 | }, |
| 3528 | | .composite_integer => return cg.todo("@abs for composite integers", .{}), |
| 4353 | .composite_integer => { |
| 4354 | const val_id = try value.materialize(cg); |
| 4355 | const scratch_top = cg.id_scratch.items.len; |
| 4356 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 4357 | const ci = try CompositeInt.init(cg, val_id, operand_info); |
| 4358 | const ci_z = try CompositeInt.zero(cg, operand_info); |
| 4359 | const is_neg = try ci.cmp(ci_z, .lt); |
| 4360 | const ci_neg = try ci_z.addSub(ci, false); |
| 4361 | const result_info = cg.arithmeticTypeInfo(result_ty); |
| 4362 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 4363 | const result_limbs = try cg.id_scratch.addManyAsSlice(cg.module.gpa, ci.n_limbs); |
| 4364 | for (0..ci.n_limbs) |i| { |
| 4365 | result_limbs[i] = cg.module.allocId(); |
| 4366 | try cg.body.emit(cg.module.gpa, .OpSelect, .{ |
| 4367 | .id_result_type = u32_ty_id, |
| 4368 | .id_result = result_limbs[i], |
| 4369 | .condition = is_neg, |
| 4370 | .object_1 = ci_neg.limbs[i], |
| 4371 | .object_2 = ci.limbs[i], |
| 4372 | }); |
| 4373 | } |
| 4374 | const ci_result = CompositeInt.fromLimbs(cg, result_limbs, result_info); |
| 4375 | const normalized = try ci_result.normalize(); |
| 4376 | return .init(result_ty, try normalized.materialize(result_ty)); |
| 4377 | }, |
| 3529 | 4378 | .bool => unreachable, |
| 3530 | 4379 | } |
| 3531 | 4380 | } |
| ... | ... | @@ -3553,7 +4402,69 @@ fn airAddSubOverflow( |
| 3553 | 4402 | |
| 3554 | 4403 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| 3555 | 4404 | switch (info.class) { |
| 3556 | | .composite_integer => return cg.todo("add/sub-with-overflow for composite integers", .{}), |
| 4405 | .composite_integer => { |
| 4406 | const lhs_id = try lhs.materialize(cg); |
| 4407 | const rhs_id = try rhs.materialize(cg); |
| 4408 | const scratch_top = cg.id_scratch.items.len; |
| 4409 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 4410 | const ci_lhs = try CompositeInt.init(cg, lhs_id, info); |
| 4411 | const ci_rhs = try CompositeInt.init(cg, rhs_id, info); |
| 4412 | const ci_sum = if (add == .OpIAdd) try ci_lhs.addSub(ci_rhs, true) else try ci_lhs.addSub(ci_rhs, false); |
| 4413 | const ci_result = try ci_sum.normalize(); |
| 4414 | const result_val_id = try ci_result.materialize(lhs.ty); |
| 4415 | |
| 4416 | const ov_bool = switch (info.signedness) { |
| 4417 | .unsigned => blk: { |
| 4418 | const ci_res2 = try CompositeInt.init(cg, result_val_id, info); |
| 4419 | const ci_lhs2 = try CompositeInt.init(cg, lhs_id, info); |
| 4420 | break :blk if (add == .OpIAdd) |
| 4421 | try ci_res2.cmp(ci_lhs2, .lt) |
| 4422 | else |
| 4423 | try ci_res2.cmp(ci_lhs2, .gt); |
| 4424 | }, |
| 4425 | .signed => blk: { |
| 4426 | const ci_res2 = try CompositeInt.init(cg, result_val_id, info); |
| 4427 | const ci_lhs2 = try CompositeInt.init(cg, lhs_id, info); |
| 4428 | const ci_rhs2 = try CompositeInt.init(cg, rhs_id, info); |
| 4429 | const ci_z = try CompositeInt.zero(cg, info); |
| 4430 | const lhs_neg = try ci_lhs2.cmp(ci_z, .lt); |
| 4431 | const rhs_neg = try ci_rhs2.cmp(ci_z, .lt); |
| 4432 | const res_neg = try ci_res2.cmp(ci_z, .lt); |
| 4433 | |
| 4434 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 4435 | const signs_match = cg.module.allocId(); |
| 4436 | try cg.body.emitRaw(cg.module.gpa, .OpLogicalEqual, 4); |
| 4437 | cg.body.writeOperand(Id, bool_ty_id); |
| 4438 | cg.body.writeOperand(Id, signs_match); |
| 4439 | cg.body.writeOperand(Id, lhs_neg); |
| 4440 | cg.body.writeOperand(Id, rhs_neg); |
| 4441 | const res_sign_diff = cg.module.allocId(); |
| 4442 | try cg.body.emitRaw(cg.module.gpa, .OpLogicalNotEqual, 4); |
| 4443 | cg.body.writeOperand(Id, bool_ty_id); |
| 4444 | cg.body.writeOperand(Id, res_sign_diff); |
| 4445 | cg.body.writeOperand(Id, lhs_neg); |
| 4446 | cg.body.writeOperand(Id, res_neg); |
| 4447 | const ov_cond = if (add == .OpIAdd) signs_match else blk2: { |
| 4448 | const not_match = cg.module.allocId(); |
| 4449 | try cg.body.emitRaw(cg.module.gpa, .OpLogicalNot, 3); |
| 4450 | cg.body.writeOperand(Id, bool_ty_id); |
| 4451 | cg.body.writeOperand(Id, not_match); |
| 4452 | cg.body.writeOperand(Id, signs_match); |
| 4453 | break :blk2 not_match; |
| 4454 | }; |
| 4455 | const ov_result = cg.module.allocId(); |
| 4456 | try cg.body.emitRaw(cg.module.gpa, .OpLogicalAnd, 4); |
| 4457 | cg.body.writeOperand(Id, bool_ty_id); |
| 4458 | cg.body.writeOperand(Id, ov_result); |
| 4459 | cg.body.writeOperand(Id, ov_cond); |
| 4460 | cg.body.writeOperand(Id, res_sign_diff); |
| 4461 | break :blk ov_result; |
| 4462 | }, |
| 4463 | }; |
| 4464 | const ov = try cg.intFromBool(.init(.bool, ov_bool), .u1); |
| 4465 | const result_ty_id = try cg.resolveType(result_ty, .direct); |
| 4466 | return try cg.constructComposite(result_ty_id, &.{ result_val_id, try ov.materialize(cg) }); |
| 4467 | }, |
| 3557 | 4468 | .strange_integer, .integer => {}, |
| 3558 | 4469 | .float, .bool => unreachable, |
| 3559 | 4470 | } |
| ... | ... | @@ -3595,6 +4506,7 @@ fn airAddSubOverflow( |
| 3595 | 4506 | |
| 3596 | 4507 | fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3597 | 4508 | const pt = cg.pt; |
| 4509 | const gpa = cg.module.gpa; |
| 3598 | 4510 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3599 | 4511 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3600 | 4512 | const lhs = try cg.temporary(extra.lhs); |
| ... | ... | @@ -3603,7 +4515,158 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3603 | 4515 | |
| 3604 | 4516 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| 3605 | 4517 | switch (info.class) { |
| 3606 | | .composite_integer => return cg.todo("mul-with-overflow for composite integers", .{}), |
| 4518 | .composite_integer => { |
| 4519 | const lhs_id = try lhs.materialize(cg); |
| 4520 | const rhs_id = try rhs.materialize(cg); |
| 4521 | const scratch_top = cg.id_scratch.items.len; |
| 4522 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 4523 | const ci_lhs = try CompositeInt.init(cg, lhs_id, info); |
| 4524 | const ci_rhs = try CompositeInt.init(cg, rhs_id, info); |
| 4525 | |
| 4526 | const low_limbs = try ci_lhs.mul(ci_rhs, false); |
| 4527 | const ci_result = try CompositeInt.fromLimbs(cg, low_limbs, info).normalize(); |
| 4528 | const result_val_id = try ci_result.materialize(lhs.ty); |
| 4529 | |
| 4530 | const ci_lhs2 = try CompositeInt.init(cg, lhs_id, info); |
| 4531 | const ci_rhs2 = try CompositeInt.init(cg, rhs_id, info); |
| 4532 | const wide_limbs = try ci_lhs2.mul(ci_rhs2, true); |
| 4533 | const high_limbs = wide_limbs[ci_lhs2.n_limbs..]; |
| 4534 | |
| 4535 | const bool_ty_id = try cg.resolveType(.bool, .direct); |
| 4536 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 4537 | const n: usize = info.backing_bits / Module.big_int_bits; |
| 4538 | |
| 4539 | const ov_bool = switch (info.signedness) { |
| 4540 | .unsigned => blk: { |
| 4541 | const zero_id = try cg.constInt(.u32, @as(u32, 0)); |
| 4542 | var any_nonzero = cg.module.allocId(); |
| 4543 | try cg.body.emitRaw(gpa, .OpINotEqual, 4); |
| 4544 | cg.body.writeOperand(Id, bool_ty_id); |
| 4545 | cg.body.writeOperand(Id, any_nonzero); |
| 4546 | cg.body.writeOperand(Id, high_limbs[0]); |
| 4547 | cg.body.writeOperand(Id, zero_id); |
| 4548 | |
| 4549 | for (1..n) |i| { |
| 4550 | const limb_nz = cg.module.allocId(); |
| 4551 | try cg.body.emitRaw(gpa, .OpINotEqual, 4); |
| 4552 | cg.body.writeOperand(Id, bool_ty_id); |
| 4553 | cg.body.writeOperand(Id, limb_nz); |
| 4554 | cg.body.writeOperand(Id, high_limbs[i]); |
| 4555 | cg.body.writeOperand(Id, zero_id); |
| 4556 | |
| 4557 | const combined = cg.module.allocId(); |
| 4558 | try cg.body.emitRaw(gpa, .OpLogicalOr, 4); |
| 4559 | cg.body.writeOperand(Id, bool_ty_id); |
| 4560 | cg.body.writeOperand(Id, combined); |
| 4561 | cg.body.writeOperand(Id, any_nonzero); |
| 4562 | cg.body.writeOperand(Id, limb_nz); |
| 4563 | any_nonzero = combined; |
| 4564 | } |
| 4565 | |
| 4566 | break :blk any_nonzero; |
| 4567 | }, |
| 4568 | .signed => blk: { |
| 4569 | const ci_res = try CompositeInt.init(cg, result_val_id, info); |
| 4570 | const top_limb = ci_res.limbs[n - 1]; |
| 4571 | const i32_ty_id = try cg.resolveType(.i32, .direct); |
| 4572 | |
| 4573 | const top_bits: u16 = if (info.bits % Module.big_int_bits == 0) |
| 4574 | Module.big_int_bits |
| 4575 | else |
| 4576 | info.bits % Module.big_int_bits; |
| 4577 | |
| 4578 | const shift_amt: u32 = top_bits - 1; |
| 4579 | const shift_id = try cg.constInt(.u32, shift_amt); |
| 4580 | |
| 4581 | const as_signed = cg.module.allocId(); |
| 4582 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 4583 | .id_result_type = i32_ty_id, |
| 4584 | .id_result = as_signed, |
| 4585 | .operand = top_limb, |
| 4586 | }); |
| 4587 | const sign_ext = cg.module.allocId(); |
| 4588 | try cg.body.emitRaw(gpa, .OpShiftRightArithmetic, 4); |
| 4589 | cg.body.writeOperand(Id, i32_ty_id); |
| 4590 | cg.body.writeOperand(Id, sign_ext); |
| 4591 | cg.body.writeOperand(Id, as_signed); |
| 4592 | cg.body.writeOperand(Id, shift_id); |
| 4593 | const expected = cg.module.allocId(); |
| 4594 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 4595 | .id_result_type = u32_ty_id, |
| 4596 | .id_result = expected, |
| 4597 | .operand = sign_ext, |
| 4598 | }); |
| 4599 | |
| 4600 | var any_mismatch = cg.module.allocId(); |
| 4601 | try cg.body.emitRaw(gpa, .OpINotEqual, 4); |
| 4602 | cg.body.writeOperand(Id, bool_ty_id); |
| 4603 | cg.body.writeOperand(Id, any_mismatch); |
| 4604 | cg.body.writeOperand(Id, high_limbs[0]); |
| 4605 | cg.body.writeOperand(Id, expected); |
| 4606 | |
| 4607 | for (1..n) |i| { |
| 4608 | const limb_ne = cg.module.allocId(); |
| 4609 | try cg.body.emitRaw(gpa, .OpINotEqual, 4); |
| 4610 | cg.body.writeOperand(Id, bool_ty_id); |
| 4611 | cg.body.writeOperand(Id, limb_ne); |
| 4612 | cg.body.writeOperand(Id, high_limbs[i]); |
| 4613 | cg.body.writeOperand(Id, expected); |
| 4614 | |
| 4615 | const combined = cg.module.allocId(); |
| 4616 | try cg.body.emitRaw(gpa, .OpLogicalOr, 4); |
| 4617 | cg.body.writeOperand(Id, bool_ty_id); |
| 4618 | cg.body.writeOperand(Id, combined); |
| 4619 | cg.body.writeOperand(Id, any_mismatch); |
| 4620 | cg.body.writeOperand(Id, limb_ne); |
| 4621 | any_mismatch = combined; |
| 4622 | } |
| 4623 | |
| 4624 | if (info.bits != info.backing_bits) { |
| 4625 | const top_bits_s: u16 = info.bits % Module.big_int_bits; |
| 4626 | const s_shift_id = try cg.constInt(.u32, top_bits_s - 1); |
| 4627 | |
| 4628 | const top_as_signed = cg.module.allocId(); |
| 4629 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 4630 | .id_result_type = i32_ty_id, |
| 4631 | .id_result = top_as_signed, |
| 4632 | .operand = top_limb, |
| 4633 | }); |
| 4634 | const top_sign_ext = cg.module.allocId(); |
| 4635 | try cg.body.emitRaw(gpa, .OpShiftRightArithmetic, 4); |
| 4636 | cg.body.writeOperand(Id, i32_ty_id); |
| 4637 | cg.body.writeOperand(Id, top_sign_ext); |
| 4638 | cg.body.writeOperand(Id, top_as_signed); |
| 4639 | cg.body.writeOperand(Id, s_shift_id); |
| 4640 | const top_expected = cg.module.allocId(); |
| 4641 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 4642 | .id_result_type = u32_ty_id, |
| 4643 | .id_result = top_expected, |
| 4644 | .operand = top_sign_ext, |
| 4645 | }); |
| 4646 | const top_mismatch = cg.module.allocId(); |
| 4647 | try cg.body.emitRaw(gpa, .OpINotEqual, 4); |
| 4648 | cg.body.writeOperand(Id, bool_ty_id); |
| 4649 | cg.body.writeOperand(Id, top_mismatch); |
| 4650 | cg.body.writeOperand(Id, top_limb); |
| 4651 | cg.body.writeOperand(Id, top_expected); |
| 4652 | |
| 4653 | const combined = cg.module.allocId(); |
| 4654 | try cg.body.emitRaw(gpa, .OpLogicalOr, 4); |
| 4655 | cg.body.writeOperand(Id, bool_ty_id); |
| 4656 | cg.body.writeOperand(Id, combined); |
| 4657 | cg.body.writeOperand(Id, any_mismatch); |
| 4658 | cg.body.writeOperand(Id, top_mismatch); |
| 4659 | any_mismatch = combined; |
| 4660 | } |
| 4661 | |
| 4662 | break :blk any_mismatch; |
| 4663 | }, |
| 4664 | }; |
| 4665 | |
| 4666 | const ov = try cg.intFromBool(.init(.bool, ov_bool), .u1); |
| 4667 | const result_ty_id = try cg.resolveType(result_ty, .direct); |
| 4668 | return try cg.constructComposite(result_ty_id, &.{ result_val_id, try ov.materialize(cg) }); |
| 4669 | }, |
| 3607 | 4670 | .strange_integer, .integer => {}, |
| 3608 | 4671 | .float, .bool => unreachable, |
| 3609 | 4672 | } |
| ... | ... | @@ -3622,7 +4685,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3622 | 4685 | 1...16 => 32, |
| 3623 | 4686 | 17...32 => if (largest_int_bits > 32) 64 else null, // Upcast if we can. |
| 3624 | 4687 | 33...64 => null, // Always use wide multiplication. |
| 3625 | | else => unreachable, // TODO: Composite integers |
| 4688 | else => unreachable, |
| 3626 | 4689 | }; |
| 3627 | 4690 | |
| 3628 | 4691 | const result, const overflowed = switch (info.signedness) { |
| ... | ... | @@ -4245,7 +5308,16 @@ fn cmp( |
| 4245 | 5308 | |
| 4246 | 5309 | const info = cg.arithmeticTypeInfo(scalar_ty); |
| 4247 | 5310 | const pred: Opcode = switch (info.class) { |
| 4248 | | .composite_integer => return cg.todo("comparison for composite integers", .{}), |
| 5311 | .composite_integer => { |
| 5312 | const lhs_id = try lhs.materialize(cg); |
| 5313 | const rhs_id = try rhs.materialize(cg); |
| 5314 | const scratch_top = cg.id_scratch.items.len; |
| 5315 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 5316 | const ci_lhs = try CompositeInt.init(cg, lhs_id, info); |
| 5317 | const ci_rhs = try CompositeInt.init(cg, rhs_id, info); |
| 5318 | const result_id = try ci_lhs.cmp(ci_rhs, op); |
| 5319 | return .init(.bool, result_id); |
| 5320 | }, |
| 4249 | 5321 | .float => switch (op) { |
| 4250 | 5322 | .eq => .OpFOrdEqual, |
| 4251 | 5323 | .neq => .OpFUnordNotEqual, |
| ... | ... | @@ -4408,6 +5480,192 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4408 | 5480 | const src_info = cg.arithmeticTypeInfo(src.ty); |
| 4409 | 5481 | const dst_info = cg.arithmeticTypeInfo(dst_ty); |
| 4410 | 5482 | |
| 5483 | const src_composite = src_info.class == .composite_integer; |
| 5484 | const dst_composite = dst_info.class == .composite_integer; |
| 5485 | |
| 5486 | if (src_composite or dst_composite) { |
| 5487 | const gpa = cg.module.gpa; |
| 5488 | const scratch_top = cg.id_scratch.items.len; |
| 5489 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 5490 | |
| 5491 | if (src_composite and dst_composite) { |
| 5492 | const src_id = try src.materialize(cg); |
| 5493 | const src_n: u16 = src_info.backing_bits / Module.big_int_bits; |
| 5494 | const dst_n: u16 = dst_info.backing_bits / Module.big_int_bits; |
| 5495 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, dst_n); |
| 5496 | const min_n = @min(src_n, dst_n); |
| 5497 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 5498 | for (0..min_n) |i| { |
| 5499 | result_limbs[i] = cg.module.allocId(); |
| 5500 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 5501 | .id_result_type = u32_ty_id, |
| 5502 | .id_result = result_limbs[i], |
| 5503 | .composite = src_id, |
| 5504 | .indexes = &.{@as(u32, @intCast(i))}, |
| 5505 | }); |
| 5506 | } |
| 5507 | if (dst_n > src_n) { |
| 5508 | const fill = if (src_info.signedness == .signed) blk: { |
| 5509 | const i32_ty_id = try cg.resolveType(.i32, .direct); |
| 5510 | const msb = result_limbs[src_n - 1]; |
| 5511 | const msb_signed = cg.module.allocId(); |
| 5512 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 5513 | .id_result_type = i32_ty_id, |
| 5514 | .id_result = msb_signed, |
| 5515 | .operand = msb, |
| 5516 | }); |
| 5517 | const shift31 = try cg.constInt(.i32, @as(i32, 31)); |
| 5518 | const sign_ext = cg.module.allocId(); |
| 5519 | try cg.body.emitRaw(gpa, .OpShiftRightArithmetic, 4); |
| 5520 | cg.body.writeOperand(Id, i32_ty_id); |
| 5521 | cg.body.writeOperand(Id, sign_ext); |
| 5522 | cg.body.writeOperand(Id, msb_signed); |
| 5523 | cg.body.writeOperand(Id, shift31); |
| 5524 | const back = cg.module.allocId(); |
| 5525 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 5526 | .id_result_type = u32_ty_id, |
| 5527 | .id_result = back, |
| 5528 | .operand = sign_ext, |
| 5529 | }); |
| 5530 | break :blk back; |
| 5531 | } else try cg.constInt(.u32, @as(u32, 0)); |
| 5532 | for (min_n..dst_n) |i| { |
| 5533 | result_limbs[i] = fill; |
| 5534 | } |
| 5535 | } |
| 5536 | const ci = CompositeInt.fromLimbs(cg, result_limbs, dst_info); |
| 5537 | const normalized = try ci.normalize(); |
| 5538 | return try normalized.materialize(dst_ty); |
| 5539 | } else if (src_composite and !dst_composite) { |
| 5540 | const src_id = try src.materialize(cg); |
| 5541 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 5542 | if (dst_info.backing_bits <= 32) { |
| 5543 | const limb0 = cg.module.allocId(); |
| 5544 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 5545 | .id_result_type = u32_ty_id, |
| 5546 | .id_result = limb0, |
| 5547 | .composite = src_id, |
| 5548 | .indexes = &.{@as(u32, 0)}, |
| 5549 | }); |
| 5550 | const tmp: Temporary = .init(.u32, limb0); |
| 5551 | const converted = try cg.buildConvert(dst_ty, tmp); |
| 5552 | const result = if (dst_info.bits < src_info.bits) |
| 5553 | try cg.normalize(converted, dst_info) |
| 5554 | else |
| 5555 | converted; |
| 5556 | return try result.materialize(cg); |
| 5557 | } else { |
| 5558 | const limb0 = cg.module.allocId(); |
| 5559 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 5560 | .id_result_type = u32_ty_id, |
| 5561 | .id_result = limb0, |
| 5562 | .composite = src_id, |
| 5563 | .indexes = &.{@as(u32, 0)}, |
| 5564 | }); |
| 5565 | const limb1 = cg.module.allocId(); |
| 5566 | try cg.body.emit(gpa, .OpCompositeExtract, .{ |
| 5567 | .id_result_type = u32_ty_id, |
| 5568 | .id_result = limb1, |
| 5569 | .composite = src_id, |
| 5570 | .indexes = &.{@as(u32, 1)}, |
| 5571 | }); |
| 5572 | const u64_ty_id = try cg.resolveType(.u64, .direct); |
| 5573 | const lo = cg.module.allocId(); |
| 5574 | try cg.body.emitRaw(gpa, .OpUConvert, 3); |
| 5575 | cg.body.writeOperand(Id, u64_ty_id); |
| 5576 | cg.body.writeOperand(Id, lo); |
| 5577 | cg.body.writeOperand(Id, limb0); |
| 5578 | const hi = cg.module.allocId(); |
| 5579 | try cg.body.emitRaw(gpa, .OpUConvert, 3); |
| 5580 | cg.body.writeOperand(Id, u64_ty_id); |
| 5581 | cg.body.writeOperand(Id, hi); |
| 5582 | cg.body.writeOperand(Id, limb1); |
| 5583 | const shift32 = try cg.constInt(.u64, @as(u64, 32)); |
| 5584 | const hi_shifted = cg.module.allocId(); |
| 5585 | try cg.body.emitRaw(gpa, .OpShiftLeftLogical, 4); |
| 5586 | cg.body.writeOperand(Id, u64_ty_id); |
| 5587 | cg.body.writeOperand(Id, hi_shifted); |
| 5588 | cg.body.writeOperand(Id, hi); |
| 5589 | cg.body.writeOperand(Id, shift32); |
| 5590 | const combined = cg.module.allocId(); |
| 5591 | try cg.body.emitRaw(gpa, .OpBitwiseOr, 4); |
| 5592 | cg.body.writeOperand(Id, u64_ty_id); |
| 5593 | cg.body.writeOperand(Id, combined); |
| 5594 | cg.body.writeOperand(Id, lo); |
| 5595 | cg.body.writeOperand(Id, hi_shifted); |
| 5596 | const tmp: Temporary = .init(.u64, combined); |
| 5597 | const converted = try cg.buildConvert(dst_ty, tmp); |
| 5598 | const result = if (dst_info.bits < src_info.bits) |
| 5599 | try cg.normalize(converted, dst_info) |
| 5600 | else |
| 5601 | converted; |
| 5602 | return try result.materialize(cg); |
| 5603 | } |
| 5604 | } else { |
| 5605 | const dst_n: u16 = dst_info.backing_bits / Module.big_int_bits; |
| 5606 | const result_limbs = try cg.id_scratch.addManyAsSlice(gpa, dst_n); |
| 5607 | const u32_ty_id = try cg.resolveType(.u32, .direct); |
| 5608 | |
| 5609 | if (src_info.backing_bits <= 32) { |
| 5610 | const converted = try cg.buildConvert(.u32, src); |
| 5611 | result_limbs[0] = try converted.materialize(cg); |
| 5612 | } else { |
| 5613 | const src_as_u64 = try cg.buildConvert(.u64, src); |
| 5614 | const src_id = try src_as_u64.materialize(cg); |
| 5615 | result_limbs[0] = cg.module.allocId(); |
| 5616 | try cg.body.emitRaw(gpa, .OpUConvert, 3); |
| 5617 | cg.body.writeOperand(Id, u32_ty_id); |
| 5618 | cg.body.writeOperand(Id, result_limbs[0]); |
| 5619 | cg.body.writeOperand(Id, src_id); |
| 5620 | const u64_ty_id = try cg.resolveType(.u64, .direct); |
| 5621 | const shift32 = try cg.constInt(.u64, @as(u64, 32)); |
| 5622 | const hi = cg.module.allocId(); |
| 5623 | try cg.body.emitRaw(gpa, .OpShiftRightLogical, 4); |
| 5624 | cg.body.writeOperand(Id, u64_ty_id); |
| 5625 | cg.body.writeOperand(Id, hi); |
| 5626 | cg.body.writeOperand(Id, src_id); |
| 5627 | cg.body.writeOperand(Id, shift32); |
| 5628 | result_limbs[1] = cg.module.allocId(); |
| 5629 | try cg.body.emitRaw(gpa, .OpUConvert, 3); |
| 5630 | cg.body.writeOperand(Id, u32_ty_id); |
| 5631 | cg.body.writeOperand(Id, result_limbs[1]); |
| 5632 | cg.body.writeOperand(Id, hi); |
| 5633 | } |
| 5634 | // Sign/zero-extend remaining limbs. |
| 5635 | const fill_start: u16 = if (src_info.backing_bits <= 32) 1 else 2; |
| 5636 | const fill = if (src_info.signedness == .signed) blk: { |
| 5637 | const i32_ty_id = try cg.resolveType(.i32, .direct); |
| 5638 | const msb = result_limbs[fill_start - 1]; |
| 5639 | const msb_signed = cg.module.allocId(); |
| 5640 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 5641 | .id_result_type = i32_ty_id, |
| 5642 | .id_result = msb_signed, |
| 5643 | .operand = msb, |
| 5644 | }); |
| 5645 | const shift31 = try cg.constInt(.i32, @as(i32, 31)); |
| 5646 | const sign_ext = cg.module.allocId(); |
| 5647 | try cg.body.emitRaw(gpa, .OpShiftRightArithmetic, 4); |
| 5648 | cg.body.writeOperand(Id, i32_ty_id); |
| 5649 | cg.body.writeOperand(Id, sign_ext); |
| 5650 | cg.body.writeOperand(Id, msb_signed); |
| 5651 | cg.body.writeOperand(Id, shift31); |
| 5652 | const back = cg.module.allocId(); |
| 5653 | try cg.body.emit(gpa, .OpBitcast, .{ |
| 5654 | .id_result_type = u32_ty_id, |
| 5655 | .id_result = back, |
| 5656 | .operand = sign_ext, |
| 5657 | }); |
| 5658 | break :blk back; |
| 5659 | } else try cg.constInt(.u32, @as(u32, 0)); |
| 5660 | for (fill_start..dst_n) |i| { |
| 5661 | result_limbs[i] = fill; |
| 5662 | } |
| 5663 | const ci = CompositeInt.fromLimbs(cg, result_limbs, dst_info); |
| 5664 | const normalized = try ci.normalize(); |
| 5665 | return try normalized.materialize(dst_ty); |
| 5666 | } |
| 5667 | } |
| 5668 | |
| 4411 | 5669 | if (src_info.backing_bits == dst_info.backing_bits) { |
| 4412 | 5670 | const result = if (dst_info.bits < src_info.bits) |
| 4413 | 5671 | try cg.normalize(src.pun(dst_ty), dst_info) |
| ... | ... | @@ -4513,7 +5771,15 @@ fn airNot(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4513 | 5771 | const result = switch (info.class) { |
| 4514 | 5772 | .bool => try cg.buildUnary(.l_not, operand), |
| 4515 | 5773 | .float => unreachable, |
| 4516 | | .composite_integer => return cg.todo("bitwise not for composite integers", .{}), |
| 5774 | .composite_integer => blk: { |
| 5775 | const op_id = try operand.materialize(cg); |
| 5776 | const scratch_top = cg.id_scratch.items.len; |
| 5777 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 5778 | const ci = try CompositeInt.init(cg, op_id, info); |
| 5779 | const notted = try ci.bitwiseNot(); |
| 5780 | const normalized = try notted.normalize(); |
| 5781 | break :blk Temporary.init(result_ty, try normalized.materialize(result_ty)); |
| 5782 | }, |
| 4517 | 5783 | .strange_integer, .integer => blk: { |
| 4518 | 5784 | const complement = try cg.buildUnary(.bit_not, operand); |
| 4519 | 5785 | break :blk try cg.normalize(complement, info); |
| ... | ... | @@ -6198,9 +7464,7 @@ fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 6198 | 7464 | const value: Value = .fromInterned(item.toInterned().?); |
| 6199 | 7465 | const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { |
| 6200 | 7466 | .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), |
| 6201 | | .@"enum" => blk: { |
| 6202 | | break :blk value.intFromEnum(zcu).toUnsignedInt(zcu); // TODO: composite integer constants |
| 6203 | | }, |
| 7467 | .@"enum" => value.intFromEnum(zcu).toUnsignedInt(zcu), |
| 6204 | 7468 | .error_set => value.getErrorInt(zcu), |
| 6205 | 7469 | .pointer => value.toUnsignedInt(zcu), |
| 6206 | 7470 | else => unreachable, |
| ... | ... | @@ -6502,7 +7766,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 6502 | 7766 | fn builtin3D( |
| 6503 | 7767 | cg: *CodeGen, |
| 6504 | 7768 | result_ty: Type, |
| 6505 | | builtin: spec.BuiltIn, |
| 7769 | built_in: spec.BuiltIn, |
| 6506 | 7770 | dimension: u32, |
| 6507 | 7771 | out_of_range_value: anytype, |
| 6508 | 7772 | ) !Id { |
| ... | ... | @@ -6511,7 +7775,7 @@ fn builtin3D( |
| 6511 | 7775 | const u32_ty_id = try cg.module.intType(.unsigned, 32); |
| 6512 | 7776 | const vec_ty_id = try cg.module.vectorType(3, u32_ty_id); |
| 6513 | 7777 | const ptr_ty_id = try cg.module.ptrType(vec_ty_id, .input); |
| 6514 | | const spv_decl_index = try cg.module.builtin(ptr_ty_id, builtin, .input); |
| 7778 | const spv_decl_index = try cg.module.builtin(ptr_ty_id, built_in, .input); |
| 6515 | 7779 | try cg.module.decl_deps.append(gpa, spv_decl_index); |
| 6516 | 7780 | const ptr_id = cg.module.declPtr(spv_decl_index).result_id; |
| 6517 | 7781 | const vec_id = cg.module.allocId(); |