aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-10-17 21:59:10 -0400
committerGitHub <noreply@github.com>2021-10-17 21:59:10 -0400
commitad17108bddc3bc198190407ab5b00820b2c17cd5 (patch)
treeb59e6657f6350aefe8a67631d8398ba584a9717e /src
parente9d1e5e533d12abe14582736d90e4cb173addc56 (diff)
parent15a0b30d8e905a7b46fa97175d9bdba2bd5a8403 (diff)
downloadzig-ad17108bddc3bc198190407ab5b00820b2c17cd5.tar.gz
zig-ad17108bddc3bc198190407ab5b00820b2c17cd5.zip
Merge pull request #9960 from Snektron/bit-not
Some not and vector stuff
Diffstat (limited to 'src')
-rw-r--r--src/Sema.zig102
-rw-r--r--src/codegen/llvm.zig98
-rw-r--r--src/codegen/llvm/bindings.zig15
-rw-r--r--src/value.zig26
4 files changed, 210 insertions, 31 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index cfc541453a..2211e69fb0 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -6629,8 +6629,42 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
const tracy = trace(@src());
defer tracy.end();
- _ = inst;
- return sema.fail(block, sema.src, "TODO implement zirBitNot", .{});
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+ const operand_src = src; // TODO put this on the operand, not the '~'
+
+ const operand = sema.resolveInst(inst_data.operand);
+ const operand_type = sema.typeOf(operand);
+ const scalar_type = operand_type.scalarType();
+
+ if (scalar_type.zigTypeTag() != .Int) {
+ return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{operand_type});
+ }
+
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ const target = sema.mod.getTarget();
+ if (val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ } else if (operand_type.zigTypeTag() == .Vector) {
+ const vec_len = operand_type.arrayLen();
+ var elem_val_buf: Value.ElemValueBuffer = undefined;
+ const elems = try sema.arena.alloc(Value, vec_len);
+ for (elems) |*elem, i| {
+ const elem_val = val.elemValueBuffer(i, &elem_val_buf);
+ elem.* = try elem_val.bitwiseNot(scalar_type, sema.arena, target);
+ }
+ return sema.addConstant(
+ operand_type,
+ try Value.Tag.array.create(sema.arena, elems),
+ );
+ } else {
+ const result_val = try val.bitwiseNot(scalar_type, sema.arena, target);
+ return sema.addConstant(scalar_type, result_val);
+ }
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addTyOp(.not, operand_type, operand);
}
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
const bool_type = Type.initTag(.bool);
const operand = try sema.coerce(block, bool_type, uncasted_operand, operand_src);
- if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
- if (val.toBool()) {
- return Air.Inst.Ref.bool_false;
- } else {
- return Air.Inst.Ref.bool_true;
- }
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ return if (val.isUndef())
+ sema.addConstUndef(bool_type)
+ else if (val.toBool())
+ Air.Inst.Ref.bool_false
+ else
+ Air.Inst.Ref.bool_true;
}
try sema.requireRuntimeBlock(block, src);
return block.addTyOp(.not, bool_type, operand);
@@ -11640,7 +11675,11 @@ fn coerce(
else => {},
},
.Array => switch (inst_ty.zigTypeTag()) {
- .Vector => return sema.coerceVectorToArray(block, dest_ty, dest_ty_src, inst, inst_src),
+ .Vector => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
+ else => {},
+ },
+ .Vector => switch (inst_ty.zigTypeTag()) {
+ .Array => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
else => {},
},
else => {},
@@ -12224,46 +12263,49 @@ fn coerceEnumToUnion(
return sema.failWithOwnedErrorMsg(msg);
}
-fn coerceVectorToArray(
+// Coerces vectors/arrays which have the same in-memory layout. This can be used for
+// both coercing from and to vectors.
+fn coerceVectorInMemory(
sema: *Sema,
block: *Block,
- array_ty: Type,
- array_ty_src: LazySrcLoc,
- vector: Air.Inst.Ref,
- vector_src: LazySrcLoc,
+ dest_ty: Type,
+ dest_ty_src: LazySrcLoc,
+ inst: Air.Inst.Ref,
+ inst_src: LazySrcLoc,
) !Air.Inst.Ref {
- const vector_ty = sema.typeOf(vector);
- const array_len = array_ty.arrayLen();
- const vector_len = vector_ty.arrayLen();
- if (array_len != vector_len) {
+ const inst_ty = sema.typeOf(inst);
+ const inst_len = inst_ty.arrayLen();
+ const dest_len = dest_ty.arrayLen();
+
+ if (dest_len != inst_len) {
const msg = msg: {
- const msg = try sema.errMsg(block, vector_src, "expected {}, found {}", .{
- array_ty, vector_ty,
+ const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
+ dest_ty, inst_ty,
});
errdefer msg.destroy(sema.gpa);
- try sema.errNote(block, array_ty_src, msg, "array has length {d}", .{array_len});
- try sema.errNote(block, vector_src, msg, "vector has length {d}", .{vector_len});
+ try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
+ try sema.errNote(block, inst_src, msg, "source has length {d}", .{inst_len});
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
}
const target = sema.mod.getTarget();
- const array_elem_ty = array_ty.childType();
- const vector_elem_ty = vector_ty.childType();
- const in_memory_result = coerceInMemoryAllowed(array_elem_ty, vector_elem_ty, false, target);
+ const dest_elem_ty = dest_ty.childType();
+ const inst_elem_ty = inst_ty.childType();
+ const in_memory_result = coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target);
if (in_memory_result != .ok) {
// TODO recursive error notes for coerceInMemoryAllowed failure
- return sema.fail(block, vector_src, "expected {}, found {}", .{ array_ty, vector_ty });
+ return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
}
- if (try sema.resolveMaybeUndefVal(block, vector_src, vector)) |vector_val| {
+ if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
// These types share the same comptime value representation.
- return sema.addConstant(array_ty, vector_val);
+ return sema.addConstant(dest_ty, inst_val);
}
- try sema.requireRuntimeBlock(block, vector_src);
- return block.addTyOp(.bitcast, array_ty, vector);
+ try sema.requireRuntimeBlock(block, inst_src);
+ return block.addTyOp(.bitcast, dest_ty, inst);
}
fn coerceCompatibleErrorSets(
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 68b0f6b635..61361b6dcc 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1299,6 +1299,66 @@ pub const DeclGen = struct {
}
return llvm_union_ty.constNamedStruct(&fields, fields.len);
},
+ .Vector => switch (tv.val.tag()) {
+ .bytes => {
+ // Note, sentinel is not stored even if the type has a sentinel.
+ const bytes = tv.val.castTag(.bytes).?.data;
+ const vector_len = tv.ty.arrayLen();
+ assert(vector_len == bytes.len or vector_len + 1 == bytes.len);
+
+ const elem_ty = tv.ty.elemType();
+ const llvm_elems = try self.gpa.alloc(*const llvm.Value, vector_len);
+ defer self.gpa.free(llvm_elems);
+ for (llvm_elems) |*elem, i| {
+ var byte_payload: Value.Payload.U64 = .{
+ .base = .{ .tag = .int_u64 },
+ .data = bytes[i],
+ };
+
+ elem.* = try self.genTypedValue(.{
+ .ty = elem_ty,
+ .val = Value.initPayload(&byte_payload.base),
+ });
+ }
+ return llvm.constVector(
+ llvm_elems.ptr,
+ @intCast(c_uint, llvm_elems.len),
+ );
+ },
+ .array => {
+ // Note, sentinel is not stored even if the type has a sentinel.
+ // The value includes the sentinel in those cases.
+ const elem_vals = tv.val.castTag(.array).?.data;
+ const vector_len = tv.ty.arrayLen();
+ assert(vector_len == elem_vals.len or vector_len + 1 == elem_vals.len);
+ const elem_ty = tv.ty.elemType();
+ const llvm_elems = try self.gpa.alloc(*const llvm.Value, vector_len);
+ defer self.gpa.free(llvm_elems);
+ for (llvm_elems) |*elem, i| {
+ elem.* = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_vals[i] });
+ }
+ return llvm.constVector(
+ llvm_elems.ptr,
+ @intCast(c_uint, llvm_elems.len),
+ );
+ },
+ .repeated => {
+ // Note, sentinel is not stored even if the type has a sentinel.
+ const val = tv.val.castTag(.repeated).?.data;
+ const elem_ty = tv.ty.elemType();
+ const len = tv.ty.arrayLen();
+ const llvm_elems = try self.gpa.alloc(*const llvm.Value, len);
+ defer self.gpa.free(llvm_elems);
+ for (llvm_elems) |*elem| {
+ elem.* = try self.genTypedValue(.{ .ty = elem_ty, .val = val });
+ }
+ return llvm.constVector(
+ llvm_elems.ptr,
+ @intCast(c_uint, llvm_elems.len),
+ );
+ },
+ else => unreachable,
+ },
.ComptimeInt => unreachable,
.ComptimeFloat => unreachable,
@@ -1313,7 +1373,6 @@ pub const DeclGen = struct {
.Frame,
.AnyFrame,
- .Vector,
=> return self.todo("implement const of type '{}'", .{tv.ty}),
}
}
@@ -2992,6 +3051,43 @@ pub const FuncGen = struct {
}
}
return array_ptr;
+ } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) {
+ const target = self.dg.module.getTarget();
+ const elem_ty = operand_ty.childType();
+ const llvm_vector_ty = try self.dg.llvmType(inst_ty);
+ if (!isByRef(operand_ty)) {
+ return self.dg.todo("implement bitcast non-ref array to vector", .{});
+ }
+
+ const bitcast_ok = elem_ty.bitSize(target) == elem_ty.abiSize(target) * 8;
+ if (bitcast_ok) {
+ const llvm_vector_ptr_ty = llvm_vector_ty.pointerType(0);
+ const casted_ptr = self.builder.buildBitCast(operand, llvm_vector_ptr_ty, "");
+ const vector = self.builder.buildLoad(casted_ptr, "");
+ // The array is aligned to the element's alignment, while the vector might have a completely
+ // different alignment. This means we need to enforce the alignment of this load.
+ vector.setAlignment(elem_ty.abiAlignment(target));
+ return vector;
+ } else {
+ // If the ABI size of the element type is not evenly divisible by size in bits;
+ // a simple bitcast will not work, and we fall back to extractelement.
+ const llvm_usize = try self.dg.llvmType(Type.usize);
+ const llvm_u32 = self.context.intType(32);
+ const zero = llvm_usize.constNull();
+ const vector_len = operand_ty.arrayLen();
+ var vector = llvm_vector_ty.getUndef();
+ var i: u64 = 0;
+ while (i < vector_len) : (i += 1) {
+ const index_usize = llvm_usize.constInt(i, .False);
+ const index_u32 = llvm_u32.constInt(i, .False);
+ const indexes: [2]*const llvm.Value = .{ zero, index_usize };
+ const elem_ptr = self.builder.buildInBoundsGEP(operand, &indexes, indexes.len, "");
+ const elem = self.builder.buildLoad(elem_ptr, "");
+ vector = self.builder.buildInsertElement(vector, elem, index_u32, "");
+ }
+
+ return vector;
+ }
}
return self.builder.buildBitCast(operand, llvm_dest_ty, "");
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index 29a46a81ee..1b7806d22a 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -313,6 +313,12 @@ pub const VerifierFailureAction = enum(c_int) {
pub const constNeg = LLVMConstNeg;
extern fn LLVMConstNeg(ConstantVal: *const Value) *const Value;
+pub const constVector = LLVMConstVector;
+extern fn LLVMConstVector(
+ ScalarConstantVals: [*]*const Value,
+ Size: c_uint,
+) *const Value;
+
pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName;
extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint;
@@ -567,6 +573,15 @@ pub const Builder = opaque {
Name: [*:0]const u8,
) *const Value;
+ pub const buildInsertElement = LLVMBuildInsertElement;
+ extern fn LLVMBuildInsertElement(
+ *const Builder,
+ VecVal: *const Value,
+ EltVal: *const Value,
+ Index: *const Value,
+ Name: [*:0]const u8,
+ ) *const Value;
+
pub const buildPtrToInt = LLVMBuildPtrToInt;
extern fn LLVMBuildPtrToInt(
*const Builder,
diff --git a/src/value.zig b/src/value.zig
index 127c6c27b4..030ee8f783 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -2081,6 +2081,32 @@ pub const Value = extern union {
};
}
+ /// operands must be integers; handles undefined.
+ pub fn bitwiseNot(val: Value, ty: Type, arena: *Allocator, target: Target) !Value {
+ if (val.isUndef()) return Value.initTag(.undef);
+
+ const info = ty.intInfo(target);
+
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var val_space: Value.BigIntSpace = undefined;
+ const val_bigint = val.toBigInt(&val_space);
+ const limbs = try arena.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcTwosCompLimbCount(info.bits),
+ );
+
+ var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
+ result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ if (result_bigint.positive) {
+ return Value.Tag.int_big_positive.create(arena, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(arena, result_limbs);
+ }
+ }
+
/// operands must be integers; handles undefined.
pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: *Allocator) !Value {
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);