authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-17 21:59:10-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-10-17 21:59:10-04:00
logad17108bddc3bc198190407ab5b00820b2c17cd5
treeb59e6657f6350aefe8a67631d8398ba584a9717e
parente9d1e5e533d12abe14582736d90e4cb173addc56
parent15a0b30d8e905a7b46fa97175d9bdba2bd5a8403
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9960 from Snektron/bit-not

Some not and vector stuff

10 files changed, 285 insertions(+), 49 deletions(-)

ci/azure/macos_arm64_script+2-1
...@@ -53,7 +53,8 @@ cmake .. \...@@ -53,7 +53,8 @@ cmake .. \
53 -DCMAKE_BUILD_TYPE=Release \53 -DCMAKE_BUILD_TYPE=Release \
54 -DZIG_TARGET_TRIPLE="$HOST_TARGET" \54 -DZIG_TARGET_TRIPLE="$HOST_TARGET" \
55 -DZIG_TARGET_MCPU="$HOST_MCPU" \55 -DZIG_TARGET_MCPU="$HOST_MCPU" \
56 -DZIG_STATIC=ON56 -DZIG_STATIC=ON \
57 -DZIG_OMIT_STAGE2=ON
5758
58unset CC59unset CC
59unset CXX60unset CXX
ci/azure/macos_script+2-1
...@@ -39,7 +39,8 @@ cmake .. \...@@ -39,7 +39,8 @@ cmake .. \
39 -DCMAKE_BUILD_TYPE=Release \39 -DCMAKE_BUILD_TYPE=Release \
40 -DZIG_TARGET_TRIPLE="$TARGET" \40 -DZIG_TARGET_TRIPLE="$TARGET" \
41 -DZIG_TARGET_MCPU="$MCPU" \41 -DZIG_TARGET_MCPU="$MCPU" \
42 -DZIG_STATIC=ON42 -DZIG_STATIC=ON \
43 -DZIG_OMIT_STAGE2=ON
4344
44# Now cmake will use zig as the C/C++ compiler. We reset the environment variables45# Now cmake will use zig as the C/C++ compiler. We reset the environment variables
45# so that installation and testing do not get affected by them.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,7 +825,7 @@ pub const Mutable = struct {
825 ///825 ///
826 /// Asserts there is enough memory to fit the result. The upper bound Limb count is826 /// Asserts there is enough memory to fit the result. The upper bound Limb count is
827 /// r is `calcTwosCompLimbCount(bit_count)`.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 // Special case: When the argument is negative, but the result is supposed to be unsigned,829 // Special case: When the argument is negative, but the result is supposed to be unsigned,
830 // return 0 in all cases.830 // return 0 in all cases.
831 if (!a.positive and signedness == .unsigned) {831 if (!a.positive and signedness == .unsigned) {
...@@ -906,6 +906,17 @@ pub const Mutable = struct {...@@ -906,6 +906,17 @@ pub const Mutable = struct {
906 r.positive = a.positive;906 r.positive = a.positive;
907 }907 }
908908
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 /// r = a | b under 2s complement semantics.920 /// r = a | b under 2s complement semantics.
910 /// r may alias with a or b.921 /// r may alias with a or b.
911 ///922 ///
...@@ -2455,7 +2466,7 @@ pub const Managed = struct {...@@ -2455,7 +2466,7 @@ pub const Managed = struct {
2455 }2466 }
24562467
2457 /// r = a <<| shift with 2s-complement saturating semantics.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 try r.ensureTwosCompCapacity(bit_count);2470 try r.ensureTwosCompCapacity(bit_count);
2460 var m = r.toMutable();2471 var m = r.toMutable();
2461 m.shiftLeftSat(a.toConst(), shift, signedness, bit_count);2472 m.shiftLeftSat(a.toConst(), shift, signedness, bit_count);
...@@ -2476,6 +2487,14 @@ pub const Managed = struct {...@@ -2476,6 +2487,14 @@ pub const Managed = struct {
2476 r.setMetadata(m.positive, m.len);2487 r.setMetadata(m.positive, m.len);
2477 }2488 }
24782489
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 /// r = a | b2498 /// r = a | b
2480 ///2499 ///
2481 /// a and b are zero-extended to the longer of a or b.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,6 +1866,42 @@ test "big.int sat shift-left signed multi negative" {
1866 try testing.expect((try a.to(SignedDoubleLimb)) == @as(SignedDoubleLimb, x) <<| shift);1866 try testing.expect((try a.to(SignedDoubleLimb)) == @as(SignedDoubleLimb, x) <<| shift);
1867}1867}
18681868
1869test "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
1878test "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
1887test "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
1896test "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
1869test "big.int bitwise and simple" {1905test "big.int bitwise and simple" {
1870 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);1906 var a = try Managed.initSet(testing.allocator, 0xffffffff11111111);
1871 defer a.deinit();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,8 +6629,42 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
6629 const tracy = trace(@src());6629 const tracy = trace(@src());
6630 defer tracy.end();6630 defer tracy.end();
66316631
6632 _ = inst;6632 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
6633 return sema.fail(block, sema.src, "TODO implement zirBitNot", .{});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}
66356669
6636fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6670fn 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,12 +8273,13 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
82398273
8240 const bool_type = Type.initTag(.bool);8274 const bool_type = Type.initTag(.bool);
8241 const operand = try sema.coerce(block, bool_type, uncasted_operand, operand_src);8275 const operand = try sema.coerce(block, bool_type, uncasted_operand, operand_src);
8242 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {8276 if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
8243 if (val.toBool()) {8277 return if (val.isUndef())
8244 return Air.Inst.Ref.bool_false;8278 sema.addConstUndef(bool_type)
8245 } else {8279 else if (val.toBool())
8246 return Air.Inst.Ref.bool_true;8280 Air.Inst.Ref.bool_false
8247 }8281 else
8282 Air.Inst.Ref.bool_true;
8248 }8283 }
8249 try sema.requireRuntimeBlock(block, src);8284 try sema.requireRuntimeBlock(block, src);
8250 return block.addTyOp(.not, bool_type, operand);8285 return block.addTyOp(.not, bool_type, operand);
...@@ -11640,7 +11675,11 @@ fn coerce(...@@ -11640,7 +11675,11 @@ fn coerce(
11640 else => {},11675 else => {},
11641 },11676 },
11642 .Array => switch (inst_ty.zigTypeTag()) {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 else => {},11683 else => {},
11645 },11684 },
11646 else => {},11685 else => {},
...@@ -12224,46 +12263,49 @@ fn coerceEnumToUnion(...@@ -12224,46 +12263,49 @@ fn coerceEnumToUnion(
12224 return sema.failWithOwnedErrorMsg(msg);12263 return sema.failWithOwnedErrorMsg(msg);
12225}12264}
1222612265
12227fn coerceVectorToArray(12266// Coerces vectors/arrays which have the same in-memory layout. This can be used for
12267// both coercing from and to vectors.
12268fn coerceVectorInMemory(
12228 sema: *Sema,12269 sema: *Sema,
12229 block: *Block,12270 block: *Block,
12230 array_ty: Type,12271 dest_ty: Type,
12231 array_ty_src: LazySrcLoc,12272 dest_ty_src: LazySrcLoc,
12232 vector: Air.Inst.Ref,12273 inst: Air.Inst.Ref,
12233 vector_src: LazySrcLoc,12274 inst_src: LazySrcLoc,
12234) !Air.Inst.Ref {12275) !Air.Inst.Ref {
12235 const vector_ty = sema.typeOf(vector);12276 const inst_ty = sema.typeOf(inst);
12236 const array_len = array_ty.arrayLen();12277 const inst_len = inst_ty.arrayLen();
12237 const vector_len = vector_ty.arrayLen();12278 const dest_len = dest_ty.arrayLen();
12238 if (array_len != vector_len) {12279
12280 if (dest_len != inst_len) {
12239 const msg = msg: {12281 const msg = msg: {
12240 const msg = try sema.errMsg(block, vector_src, "expected {}, found {}", .{12282 const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
12241 array_ty, vector_ty,12283 dest_ty, inst_ty,
12242 });12284 });
12243 errdefer msg.destroy(sema.gpa);12285 errdefer msg.destroy(sema.gpa);
12244 try sema.errNote(block, array_ty_src, msg, "array has length {d}", .{array_len});12286 try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
12245 try sema.errNote(block, vector_src, msg, "vector has length {d}", .{vector_len});12287 try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
12246 break :msg msg;12288 break :msg msg;
12247 };12289 };
12248 return sema.failWithOwnedErrorMsg(msg);12290 return sema.failWithOwnedErrorMsg(msg);
12249 }12291 }
1225012292
12251 const target = sema.mod.getTarget();12293 const target = sema.mod.getTarget();
12252 const array_elem_ty = array_ty.childType();12294 const dest_elem_ty = dest_ty.childType();
12253 const vector_elem_ty = vector_ty.childType();12295 const inst_elem_ty = inst_ty.childType();
12254 const in_memory_result = coerceInMemoryAllowed(array_elem_ty, vector_elem_ty, false, target);12296 const in_memory_result = coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target);
12255 if (in_memory_result != .ok) {12297 if (in_memory_result != .ok) {
12256 // TODO recursive error notes for coerceInMemoryAllowed failure12298 // 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 }
1225912301
12260 if (try sema.resolveMaybeUndefVal(block, vector_src, vector)) |vector_val| {12302 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
12261 // These types share the same comptime value representation.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 }
1226412306
12265 try sema.requireRuntimeBlock(block, vector_src);12307 try sema.requireRuntimeBlock(block, inst_src);
12266 return block.addTyOp(.bitcast, array_ty, vector);12308 return block.addTyOp(.bitcast, dest_ty, inst);
12267}12309}
1226812310
12269fn coerceCompatibleErrorSets(12311fn coerceCompatibleErrorSets(
src/codegen/llvm.zig+97-1
...@@ -1299,6 +1299,66 @@ pub const DeclGen = struct {...@@ -1299,6 +1299,66 @@ pub const DeclGen = struct {
1299 }1299 }
1300 return llvm_union_ty.constNamedStruct(&fields, fields.len);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 },
13021362
1303 .ComptimeInt => unreachable,1363 .ComptimeInt => unreachable,
1304 .ComptimeFloat => unreachable,1364 .ComptimeFloat => unreachable,
...@@ -1313,7 +1373,6 @@ pub const DeclGen = struct {...@@ -1313,7 +1373,6 @@ pub const DeclGen = struct {
13131373
1314 .Frame,1374 .Frame,
1315 .AnyFrame,1375 .AnyFrame,
1316 .Vector,
1317 => return self.todo("implement const of type '{}'", .{tv.ty}),1376 => return self.todo("implement const of type '{}'", .{tv.ty}),
1318 }1377 }
1319 }1378 }
...@@ -2992,6 +3051,43 @@ pub const FuncGen = struct {...@@ -2992,6 +3051,43 @@ pub const FuncGen = struct {
2992 }3051 }
2993 }3052 }
2994 return array_ptr;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 }
29963092
2997 return self.builder.buildBitCast(operand, llvm_dest_ty, "");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,6 +313,12 @@ pub const VerifierFailureAction = enum(c_int) {
313pub const constNeg = LLVMConstNeg;313pub const constNeg = LLVMConstNeg;
314extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value;314extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value;
315315
316pub const constVector = LLVMConstVector;
317extern fn LLVMConstVector(
318 ScalarConstantVals: [*]*const Value,
319 Size: c_uint,
320) *const Value;
321
316pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;322pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;
317extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;323extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;
318324
...@@ -567,6 +573,15 @@ pub const Builder = opaque {...@@ -567,6 +573,15 @@ pub const Builder = opaque {
567 Name: [*:0]const u8,573 Name: [*:0]const u8,
568 ) *const Value;574 ) *const Value;
569575
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 pub const buildPtrToInt = LLVMBuildPtrToInt;585 pub const buildPtrToInt = LLVMBuildPtrToInt;
571 extern fn LLVMBuildPtrToInt(586 extern fn LLVMBuildPtrToInt(
572 *const Builder,587 *const Builder,
src/value.zig+26
...@@ -2081,6 +2081,32 @@ pub const Value = extern union {...@@ -2081,6 +2081,32 @@ pub const Value = extern union {
2081 };2081 };
2082 }2082 }
20832083
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 /// operands must be integers; handles undefined. 2110 /// operands must be integers; handles undefined.
2085 pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value {2111 pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value {
2086 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);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,3 +235,17 @@ test "comptime_int param and return" {
235fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {235fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
236 return a + b;236 return a + b;
237}237}
238
239test "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
249fn testBinaryNot(x: u16) !void {
250 try expect(~x == 0b0101010101010101);
251}
test/behavior/math_stage1.zig-14
...@@ -219,20 +219,6 @@ const DivResult = struct {...@@ -219,20 +219,6 @@ const DivResult = struct {
219 remainder: u64,219 remainder: u64,
220};220};
221221
222test "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
232fn testBinaryNot(x: u16) !void {
233 try expect(~x == 0b0101010101010101);
234}
235
236test "small int addition" {222test "small int addition" {
237 var x: u2 = 0;223 var x: u2 = 0;
238 try expect(x == 0);224 try expect(x == 0);