| author | |
| committer | |
| log | ad17108bddc3bc198190407ab5b00820b2c17cd5 |
| tree | b59e6657f6350aefe8a67631d8398ba584a9717e |
| parent | e9d1e5e533d12abe14582736d90e4cb173addc56 |
| parent | 15a0b30d8e905a7b46fa97175d9bdba2bd5a8403 |
| signature |
Some not and vector stuff10 files changed, 285 insertions(+), 49 deletions(-)
ci/azure/macos_arm64_script+2-1| ... | ... | @@ -53,7 +53,8 @@ cmake .. \ |
| 53 | 53 | -DCMAKE_BUILD_TYPE=Release \ |
| 54 | 54 | -DZIG_TARGET_TRIPLE="$HOST_TARGET" \ |
| 55 | 55 | -DZIG_TARGET_MCPU="$HOST_MCPU" \ |
| 56 | -DZIG_STATIC=ON | |
| 56 | -DZIG_STATIC=ON \ | |
| 57 | -DZIG_OMIT_STAGE2=ON | |
| 57 | 58 | |
| 58 | 59 | unset CC |
| 59 | 60 | unset CXX |
ci/azure/macos_script+2-1| ... | ... | @@ -39,7 +39,8 @@ cmake .. \ |
| 39 | 39 | -DCMAKE_BUILD_TYPE=Release \ |
| 40 | 40 | -DZIG_TARGET_TRIPLE="$TARGET" \ |
| 41 | 41 | -DZIG_TARGET_MCPU="$MCPU" \ |
| 42 | -DZIG_STATIC=ON | |
| 42 | -DZIG_STATIC=ON \ | |
| 43 | -DZIG_OMIT_STAGE2=ON | |
| 43 | 44 | |
| 44 | 45 | # Now cmake will use zig as the C/C++ compiler. We reset the environment variables |
| 45 | 46 | # so that installation and testing do not get affected by them. |
lib/std/math/big/int.zig+21-2| ... | ... | @@ -825,7 +825,7 @@ pub const Mutable = struct { |
| 825 | 825 | /// |
| 826 | 826 | /// Asserts there is enough memory to fit the result. The upper bound Limb count is |
| 827 | 827 | /// r is `calcTwosCompLimbCount(bit_count)`. |
| 828 | pub fn shiftLeftSat(r: *Mutable, a: Const, shift: usize, signedness: std.builtin.Signedness, bit_count: usize) void { | |
| 828 | pub fn shiftLeftSat(r: *Mutable, a: Const, shift: usize, signedness: Signedness, bit_count: usize) void { | |
| 829 | 829 | // Special case: When the argument is negative, but the result is supposed to be unsigned, |
| 830 | 830 | // return 0 in all cases. |
| 831 | 831 | if (!a.positive and signedness == .unsigned) { |
| ... | ... | @@ -906,6 +906,17 @@ pub const Mutable = struct { |
| 906 | 906 | r.positive = a.positive; |
| 907 | 907 | } |
| 908 | 908 | |
| 909 | /// r = ~a under 2s complement wrapping semantics. | |
| 910 | /// r may alias with a. | |
| 911 | /// | |
| 912 | /// Assets that r has enough limbs to store the result. The upper bound Limb count is | |
| 913 | /// r is `calcTwosCompLimbCount(bit_count)`. | |
| 914 | pub fn bitNotWrap(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void { | |
| 915 | r.copy(a.negate()); | |
| 916 | const negative_one = Const{ .limbs = &.{1}, .positive = false }; | |
| 917 | r.addWrap(r.toConst(), negative_one, signedness, bit_count); | |
| 918 | } | |
| 919 | ||
| 909 | 920 | /// r = a | b under 2s complement semantics. |
| 910 | 921 | /// r may alias with a or b. |
| 911 | 922 | /// |
| ... | ... | @@ -2455,7 +2466,7 @@ pub const Managed = struct { |
| 2455 | 2466 | } |
| 2456 | 2467 | |
| 2457 | 2468 | /// r = a <<| shift with 2s-complement saturating semantics. |
| 2458 | pub fn shiftLeftSat(r: *Managed, a: Managed, shift: usize, signedness: std.builtin.Signedness, bit_count: usize) !void { | |
| 2469 | pub fn shiftLeftSat(r: *Managed, a: Managed, shift: usize, signedness: Signedness, bit_count: usize) !void { | |
| 2459 | 2470 | try r.ensureTwosCompCapacity(bit_count); |
| 2460 | 2471 | var m = r.toMutable(); |
| 2461 | 2472 | m.shiftLeftSat(a.toConst(), shift, signedness, bit_count); |
| ... | ... | @@ -2476,6 +2487,14 @@ pub const Managed = struct { |
| 2476 | 2487 | r.setMetadata(m.positive, m.len); |
| 2477 | 2488 | } |
| 2478 | 2489 | |
| 2490 | /// r = ~a under 2s-complement wrapping semantics. | |
| 2491 | pub fn bitNotWrap(r: *Managed, a: Managed, signedness: Signedness, bit_count: usize) !void { | |
| 2492 | try r.ensureTwosCompCapacity(bit_count); | |
| 2493 | var m = r.toMutable(); | |
| 2494 | m.bitNotWrap(a.toConst(), signedness, bit_count); | |
| 2495 | r.setMetadata(m.positive, m.len); | |
| 2496 | } | |
| 2497 | ||
| 2479 | 2498 | /// r = a | b |
| 2480 | 2499 | /// |
| 2481 | 2500 | /// a and b are zero-extended to the longer of a or b. |
lib/std/math/big/int_test.zig+36| ... | ... | @@ -1866,6 +1866,42 @@ test "big.int sat shift-left signed multi negative" { |
| 1866 | 1866 | try testing.expect((try a.to(SignedDoubleLimb)) == @as(SignedDoubleLimb, x) <<| shift); |
| 1867 | 1867 | } |
| 1868 | 1868 | |
| 1869 | test "big.int bitNotWrap unsigned simple" { | |
| 1870 | var a = try Managed.initSet(testing.allocator, 123); | |
| 1871 | defer a.deinit(); | |
| 1872 | ||
| 1873 | try a.bitNotWrap(a, .unsigned, 10); | |
| 1874 | ||
| 1875 | try testing.expect((try a.to(u10)) == ~@as(u10, 123)); | |
| 1876 | } | |
| 1877 | ||
| 1878 | test "big.int bitNotWrap unsigned multi" { | |
| 1879 | var a = try Managed.initSet(testing.allocator, 0); | |
| 1880 | defer a.deinit(); | |
| 1881 | ||
| 1882 | try a.bitNotWrap(a, .unsigned, @bitSizeOf(DoubleLimb)); | |
| 1883 | ||
| 1884 | try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb)); | |
| 1885 | } | |
| 1886 | ||
| 1887 | test "big.int bitNotWrap signed simple" { | |
| 1888 | var a = try Managed.initSet(testing.allocator, -456); | |
| 1889 | defer a.deinit(); | |
| 1890 | ||
| 1891 | try a.bitNotWrap(a, .signed, 11); | |
| 1892 | ||
| 1893 | try testing.expect((try a.to(i11)) == ~@as(i11, -456)); | |
| 1894 | } | |
| 1895 | ||
| 1896 | test "big.int bitNotWrap signed multi" { | |
| 1897 | var a = try Managed.initSet(testing.allocator, 0); | |
| 1898 | defer a.deinit(); | |
| 1899 | ||
| 1900 | try a.bitNotWrap(a, .signed, @bitSizeOf(SignedDoubleLimb)); | |
| 1901 | ||
| 1902 | try testing.expect((try a.to(SignedDoubleLimb)) == -1); | |
| 1903 | } | |
| 1904 | ||
| 1869 | 1905 | test "big.int bitwise and simple" { |
| 1870 | 1906 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); |
| 1871 | 1907 | defer a.deinit(); |
src/Sema.zig+72-30| ... | ... | @@ -6629,8 +6629,42 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 6629 | 6629 | const tracy = trace(@src()); |
| 6630 | 6630 | defer tracy.end(); |
| 6631 | 6631 | |
| 6632 | _ = inst; | |
| 6633 | return sema.fail(block, sema.src, "TODO implement zirBitNot", .{}); | |
| 6632 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 6633 | const src = inst_data.src(); | |
| 6634 | const operand_src = src; // TODO put this on the operand, not the '~' | |
| 6635 | ||
| 6636 | const operand = sema.resolveInst(inst_data.operand); | |
| 6637 | const operand_type = sema.typeOf(operand); | |
| 6638 | const scalar_type = operand_type.scalarType(); | |
| 6639 | ||
| 6640 | if (scalar_type.zigTypeTag() != .Int) { | |
| 6641 | return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{operand_type}); | |
| 6642 | } | |
| 6643 | ||
| 6644 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 6645 | const target = sema.mod.getTarget(); | |
| 6646 | if (val.isUndef()) { | |
| 6647 | return sema.addConstUndef(scalar_type); | |
| 6648 | } else if (operand_type.zigTypeTag() == .Vector) { | |
| 6649 | const vec_len = operand_type.arrayLen(); | |
| 6650 | var elem_val_buf: Value.ElemValueBuffer = undefined; | |
| 6651 | const elems = try sema.arena.alloc(Value, vec_len); | |
| 6652 | for (elems) |*elem, i| { | |
| 6653 | const elem_val = val.elemValueBuffer(i, &elem_val_buf); | |
| 6654 | elem.* = try elem_val.bitwiseNot(scalar_type, sema.arena, target); | |
| 6655 | } | |
| 6656 | return sema.addConstant( | |
| 6657 | operand_type, | |
| 6658 | try Value.Tag.array.create(sema.arena, elems), | |
| 6659 | ); | |
| 6660 | } else { | |
| 6661 | const result_val = try val.bitwiseNot(scalar_type, sema.arena, target); | |
| 6662 | return sema.addConstant(scalar_type, result_val); | |
| 6663 | } | |
| 6664 | } | |
| 6665 | ||
| 6666 | try sema.requireRuntimeBlock(block, src); | |
| 6667 | return block.addTyOp(.not, operand_type, operand); | |
| 6634 | 6668 | } |
| 6635 | 6669 | |
| 6636 | 6670 | fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -8239,12 +8273,13 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 8239 | 8273 | |
| 8240 | 8274 | const bool_type = Type.initTag(.bool); |
| 8241 | 8275 | const operand = try sema.coerce(block, bool_type, uncasted_operand, operand_src); |
| 8242 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { | |
| 8243 | if (val.toBool()) { | |
| 8244 | return Air.Inst.Ref.bool_false; | |
| 8245 | } else { | |
| 8246 | return Air.Inst.Ref.bool_true; | |
| 8247 | } | |
| 8276 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | |
| 8277 | return if (val.isUndef()) | |
| 8278 | sema.addConstUndef(bool_type) | |
| 8279 | else if (val.toBool()) | |
| 8280 | Air.Inst.Ref.bool_false | |
| 8281 | else | |
| 8282 | Air.Inst.Ref.bool_true; | |
| 8248 | 8283 | } |
| 8249 | 8284 | try sema.requireRuntimeBlock(block, src); |
| 8250 | 8285 | return block.addTyOp(.not, bool_type, operand); |
| ... | ... | @@ -11640,7 +11675,11 @@ fn coerce( |
| 11640 | 11675 | else => {}, |
| 11641 | 11676 | }, |
| 11642 | 11677 | .Array => switch (inst_ty.zigTypeTag()) { |
| 11643 | .Vector => return sema.coerceVectorToArray(block, dest_ty, dest_ty_src, inst, inst_src), | |
| 11678 | .Vector => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src), | |
| 11679 | else => {}, | |
| 11680 | }, | |
| 11681 | .Vector => switch (inst_ty.zigTypeTag()) { | |
| 11682 | .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src), | |
| 11644 | 11683 | else => {}, |
| 11645 | 11684 | }, |
| 11646 | 11685 | else => {}, |
| ... | ... | @@ -12224,46 +12263,49 @@ fn coerceEnumToUnion( |
| 12224 | 12263 | return sema.failWithOwnedErrorMsg(msg); |
| 12225 | 12264 | } |
| 12226 | 12265 | |
| 12227 | fn coerceVectorToArray( | |
| 12266 | // Coerces vectors/arrays which have the same in-memory layout. This can be used for | |
| 12267 | // both coercing from and to vectors. | |
| 12268 | fn coerceVectorInMemory( | |
| 12228 | 12269 | sema: *Sema, |
| 12229 | 12270 | block: *Block, |
| 12230 | array_ty: Type, | |
| 12231 | array_ty_src: LazySrcLoc, | |
| 12232 | vector: Air.Inst.Ref, | |
| 12233 | vector_src: LazySrcLoc, | |
| 12271 | dest_ty: Type, | |
| 12272 | dest_ty_src: LazySrcLoc, | |
| 12273 | inst: Air.Inst.Ref, | |
| 12274 | inst_src: LazySrcLoc, | |
| 12234 | 12275 | ) !Air.Inst.Ref { |
| 12235 | const vector_ty = sema.typeOf(vector); | |
| 12236 | const array_len = array_ty.arrayLen(); | |
| 12237 | const vector_len = vector_ty.arrayLen(); | |
| 12238 | if (array_len != vector_len) { | |
| 12276 | const inst_ty = sema.typeOf(inst); | |
| 12277 | const inst_len = inst_ty.arrayLen(); | |
| 12278 | const dest_len = dest_ty.arrayLen(); | |
| 12279 | ||
| 12280 | if (dest_len != inst_len) { | |
| 12239 | 12281 | const msg = msg: { |
| 12240 | const msg = try sema.errMsg(block, vector_src, "expected {}, found {}", .{ | |
| 12241 | array_ty, vector_ty, | |
| 12282 | const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{ | |
| 12283 | dest_ty, inst_ty, | |
| 12242 | 12284 | }); |
| 12243 | 12285 | errdefer msg.destroy(sema.gpa); |
| 12244 | try sema.errNote(block, array_ty_src, msg, "array has length {d}", .{array_len}); | |
| 12245 | try sema.errNote(block, vector_src, msg, "vector has length {d}", .{vector_len}); | |
| 12286 | try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len}); | |
| 12287 | try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len}); | |
| 12246 | 12288 | break :msg msg; |
| 12247 | 12289 | }; |
| 12248 | 12290 | return sema.failWithOwnedErrorMsg(msg); |
| 12249 | 12291 | } |
| 12250 | 12292 | |
| 12251 | 12293 | const target = sema.mod.getTarget(); |
| 12252 | const array_elem_ty = array_ty.childType(); | |
| 12253 | const vector_elem_ty = vector_ty.childType(); | |
| 12254 | const in_memory_result = coerceInMemoryAllowed(array_elem_ty, vector_elem_ty, false, target); | |
| 12294 | const dest_elem_ty = dest_ty.childType(); | |
| 12295 | const inst_elem_ty = inst_ty.childType(); | |
| 12296 | const in_memory_result = coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target); | |
| 12255 | 12297 | if (in_memory_result != .ok) { |
| 12256 | 12298 | // TODO recursive error notes for coerceInMemoryAllowed failure |
| 12257 | return sema.fail(block, vector_src, "expected {}, found {}", .{ array_ty, vector_ty }); | |
| 12299 | return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty }); | |
| 12258 | 12300 | } |
| 12259 | 12301 | |
| 12260 | if (try sema.resolveMaybeUndefVal(block, vector_src, vector)) |vector_val| { | |
| 12302 | if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| { | |
| 12261 | 12303 | // These types share the same comptime value representation. |
| 12262 | return sema.addConstant(array_ty, vector_val); | |
| 12304 | return sema.addConstant(dest_ty, inst_val); | |
| 12263 | 12305 | } |
| 12264 | 12306 | |
| 12265 | try sema.requireRuntimeBlock(block, vector_src); | |
| 12266 | return block.addTyOp(.bitcast, array_ty, vector); | |
| 12307 | try sema.requireRuntimeBlock(block, inst_src); | |
| 12308 | return block.addTyOp(.bitcast, dest_ty, inst); | |
| 12267 | 12309 | } |
| 12268 | 12310 | |
| 12269 | 12311 | fn coerceCompatibleErrorSets( |
src/codegen/llvm.zig+97-1| ... | ... | @@ -1299,6 +1299,66 @@ pub const DeclGen = struct { |
| 1299 | 1299 | } |
| 1300 | 1300 | return llvm_union_ty.constNamedStruct(&fields, fields.len); |
| 1301 | 1301 | }, |
| 1302 | .Vector => switch (tv.val.tag()) { | |
| 1303 | .bytes => { | |
| 1304 | // Note, sentinel is not stored even if the type has a sentinel. | |
| 1305 | const bytes = tv.val.castTag(.bytes).?.data; | |
| 1306 | const vector_len = tv.ty.arrayLen(); | |
| 1307 | assert(vector_len == bytes.len or vector_len + 1 == bytes.len); | |
| 1308 | ||
| 1309 | const elem_ty = tv.ty.elemType(); | |
| 1310 | const llvm_elems = try self.gpa.alloc(*const llvm.Value, vector_len); | |
| 1311 | defer self.gpa.free(llvm_elems); | |
| 1312 | for (llvm_elems) |*elem, i| { | |
| 1313 | var byte_payload: Value.Payload.U64 = .{ | |
| 1314 | .base = .{ .tag = .int_u64 }, | |
| 1315 | .data = bytes[i], | |
| 1316 | }; | |
| 1317 | ||
| 1318 | elem.* = try self.genTypedValue(.{ | |
| 1319 | .ty = elem_ty, | |
| 1320 | .val = Value.initPayload(&byte_payload.base), | |
| 1321 | }); | |
| 1322 | } | |
| 1323 | return llvm.constVector( | |
| 1324 | llvm_elems.ptr, | |
| 1325 | @intCast(c_uint, llvm_elems.len), | |
| 1326 | ); | |
| 1327 | }, | |
| 1328 | .array => { | |
| 1329 | // Note, sentinel is not stored even if the type has a sentinel. | |
| 1330 | // The value includes the sentinel in those cases. | |
| 1331 | const elem_vals = tv.val.castTag(.array).?.data; | |
| 1332 | const vector_len = tv.ty.arrayLen(); | |
| 1333 | assert(vector_len == elem_vals.len or vector_len + 1 == elem_vals.len); | |
| 1334 | const elem_ty = tv.ty.elemType(); | |
| 1335 | const llvm_elems = try self.gpa.alloc(*const llvm.Value, vector_len); | |
| 1336 | defer self.gpa.free(llvm_elems); | |
| 1337 | for (llvm_elems) |*elem, i| { | |
| 1338 | elem.* = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_vals[i] }); | |
| 1339 | } | |
| 1340 | return llvm.constVector( | |
| 1341 | llvm_elems.ptr, | |
| 1342 | @intCast(c_uint, llvm_elems.len), | |
| 1343 | ); | |
| 1344 | }, | |
| 1345 | .repeated => { | |
| 1346 | // Note, sentinel is not stored even if the type has a sentinel. | |
| 1347 | const val = tv.val.castTag(.repeated).?.data; | |
| 1348 | const elem_ty = tv.ty.elemType(); | |
| 1349 | const len = tv.ty.arrayLen(); | |
| 1350 | const llvm_elems = try self.gpa.alloc(*const llvm.Value, len); | |
| 1351 | defer self.gpa.free(llvm_elems); | |
| 1352 | for (llvm_elems) |*elem| { | |
| 1353 | elem.* = try self.genTypedValue(.{ .ty = elem_ty, .val = val }); | |
| 1354 | } | |
| 1355 | return llvm.constVector( | |
| 1356 | llvm_elems.ptr, | |
| 1357 | @intCast(c_uint, llvm_elems.len), | |
| 1358 | ); | |
| 1359 | }, | |
| 1360 | else => unreachable, | |
| 1361 | }, | |
| 1302 | 1362 | |
| 1303 | 1363 | .ComptimeInt => unreachable, |
| 1304 | 1364 | .ComptimeFloat => unreachable, |
| ... | ... | @@ -1313,7 +1373,6 @@ pub const DeclGen = struct { |
| 1313 | 1373 | |
| 1314 | 1374 | .Frame, |
| 1315 | 1375 | .AnyFrame, |
| 1316 | .Vector, | |
| 1317 | 1376 | => return self.todo("implement const of type '{}'", .{tv.ty}), |
| 1318 | 1377 | } |
| 1319 | 1378 | } |
| ... | ... | @@ -2992,6 +3051,43 @@ pub const FuncGen = struct { |
| 2992 | 3051 | } |
| 2993 | 3052 | } |
| 2994 | 3053 | return array_ptr; |
| 3054 | } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) { | |
| 3055 | const target = self.dg.module.getTarget(); | |
| 3056 | const elem_ty = operand_ty.childType(); | |
| 3057 | const llvm_vector_ty = try self.dg.llvmType(inst_ty); | |
| 3058 | if (!isByRef(operand_ty)) { | |
| 3059 | return self.dg.todo("implement bitcast non-ref array to vector", .{}); | |
| 3060 | } | |
| 3061 | ||
| 3062 | const bitcast_ok = elem_ty.bitSize(target) == elem_ty.abiSize(target) * 8; | |
| 3063 | if (bitcast_ok) { | |
| 3064 | const llvm_vector_ptr_ty = llvm_vector_ty.pointerType(0); | |
| 3065 | const casted_ptr = self.builder.buildBitCast(operand, llvm_vector_ptr_ty, ""); | |
| 3066 | const vector = self.builder.buildLoad(casted_ptr, ""); | |
| 3067 | // The array is aligned to the element's alignment, while the vector might have a completely | |
| 3068 | // different alignment. This means we need to enforce the alignment of this load. | |
| 3069 | vector.setAlignment(elem_ty.abiAlignment(target)); | |
| 3070 | return vector; | |
| 3071 | } else { | |
| 3072 | // If the ABI size of the element type is not evenly divisible by size in bits; | |
| 3073 | // a simple bitcast will not work, and we fall back to extractelement. | |
| 3074 | const llvm_usize = try self.dg.llvmType(Type.usize); | |
| 3075 | const llvm_u32 = self.context.intType(32); | |
| 3076 | const zero = llvm_usize.constNull(); | |
| 3077 | const vector_len = operand_ty.arrayLen(); | |
| 3078 | var vector = llvm_vector_ty.getUndef(); | |
| 3079 | var i: u64 = 0; | |
| 3080 | while (i < vector_len) : (i += 1) { | |
| 3081 | const index_usize = llvm_usize.constInt(i, .False); | |
| 3082 | const index_u32 = llvm_u32.constInt(i, .False); | |
| 3083 | const indexes: [2]*const llvm.Value = .{ zero, index_usize }; | |
| 3084 | const elem_ptr = self.builder.buildInBoundsGEP(operand, &indexes, indexes.len, ""); | |
| 3085 | const elem = self.builder.buildLoad(elem_ptr, ""); | |
| 3086 | vector = self.builder.buildInsertElement(vector, elem, index_u32, ""); | |
| 3087 | } | |
| 3088 | ||
| 3089 | return vector; | |
| 3090 | } | |
| 2995 | 3091 | } |
| 2996 | 3092 | |
| 2997 | 3093 | return self.builder.buildBitCast(operand, llvm_dest_ty, ""); |
src/codegen/llvm/bindings.zig+15| ... | ... | @@ -313,6 +313,12 @@ pub const VerifierFailureAction = enum(c_int) { |
| 313 | 313 | pub const constNeg = LLVMConstNeg; |
| 314 | 314 | extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value; |
| 315 | 315 | |
| 316 | pub const constVector = LLVMConstVector; | |
| 317 | extern fn LLVMConstVector( | |
| 318 | ScalarConstantVals: [*]*const Value, | |
| 319 | Size: c_uint, | |
| 320 | ) *const Value; | |
| 321 | ||
| 316 | 322 | pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; |
| 317 | 323 | extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; |
| 318 | 324 | |
| ... | ... | @@ -567,6 +573,15 @@ pub const Builder = opaque { |
| 567 | 573 | Name: [*:0]const u8, |
| 568 | 574 | ) *const Value; |
| 569 | 575 | |
| 576 | pub const buildInsertElement = LLVMBuildInsertElement; | |
| 577 | extern fn LLVMBuildInsertElement( | |
| 578 | *const Builder, | |
| 579 | VecVal: *const Value, | |
| 580 | EltVal: *const Value, | |
| 581 | Index: *const Value, | |
| 582 | Name: [*:0]const u8, | |
| 583 | ) *const Value; | |
| 584 | ||
| 570 | 585 | pub const buildPtrToInt = LLVMBuildPtrToInt; |
| 571 | 586 | extern fn LLVMBuildPtrToInt( |
| 572 | 587 | *const Builder, |
src/value.zig+26| ... | ... | @@ -2081,6 +2081,32 @@ pub const Value = extern union { |
| 2081 | 2081 | }; |
| 2082 | 2082 | } |
| 2083 | 2083 | |
| 2084 | /// operands must be integers; handles undefined. | |
| 2085 | pub fn bitwiseNot(val: Value, ty: Type, arena: *Allocator, target: Target) !Value { | |
| 2086 | if (val.isUndef()) return Value.initTag(.undef); | |
| 2087 | ||
| 2088 | const info = ty.intInfo(target); | |
| 2089 | ||
| 2090 | // TODO is this a performance issue? maybe we should try the operation without | |
| 2091 | // resorting to BigInt first. | |
| 2092 | var val_space: Value.BigIntSpace = undefined; | |
| 2093 | const val_bigint = val.toBigInt(&val_space); | |
| 2094 | const limbs = try arena.alloc( | |
| 2095 | std.math.big.Limb, | |
| 2096 | std.math.big.int.calcTwosCompLimbCount(info.bits), | |
| 2097 | ); | |
| 2098 | ||
| 2099 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 2100 | result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits); | |
| 2101 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 2102 | ||
| 2103 | if (result_bigint.positive) { | |
| 2104 | return Value.Tag.int_big_positive.create(arena, result_limbs); | |
| 2105 | } else { | |
| 2106 | return Value.Tag.int_big_negative.create(arena, result_limbs); | |
| 2107 | } | |
| 2108 | } | |
| 2109 | ||
| 2084 | 2110 | /// operands must be integers; handles undefined. |
| 2085 | 2111 | pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value { |
| 2086 | 2112 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
test/behavior/math.zig+14| ... | ... | @@ -235,3 +235,17 @@ test "comptime_int param and return" { |
| 235 | 235 | fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { |
| 236 | 236 | return a + b; |
| 237 | 237 | } |
| 238 | ||
| 239 | test "binary not" { | |
| 240 | try expect(comptime x: { | |
| 241 | break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; | |
| 242 | }); | |
| 243 | try expect(comptime x: { | |
| 244 | break :x ~@as(u64, 2147483647) == 18446744071562067968; | |
| 245 | }); | |
| 246 | try testBinaryNot(0b1010101010101010); | |
| 247 | } | |
| 248 | ||
| 249 | fn testBinaryNot(x: u16) !void { | |
| 250 | try expect(~x == 0b0101010101010101); | |
| 251 | } |
test/behavior/math_stage1.zig-14| ... | ... | @@ -219,20 +219,6 @@ const DivResult = struct { |
| 219 | 219 | remainder: u64, |
| 220 | 220 | }; |
| 221 | 221 | |
| 222 | test "binary not" { | |
| 223 | try expect(comptime x: { | |
| 224 | break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; | |
| 225 | }); | |
| 226 | try expect(comptime x: { | |
| 227 | break :x ~@as(u64, 2147483647) == 18446744071562067968; | |
| 228 | }); | |
| 229 | try testBinaryNot(0b1010101010101010); | |
| 230 | } | |
| 231 | ||
| 232 | fn testBinaryNot(x: u16) !void { | |
| 233 | try expect(~x == 0b0101010101010101); | |
| 234 | } | |
| 235 | ||
| 236 | 222 | test "small int addition" { |
| 237 | 223 | var x: u2 = 0; |
| 238 | 224 | try expect(x == 0); |