diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-10-17 21:59:10 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-17 21:59:10 -0400 |
| commit | ad17108bddc3bc198190407ab5b00820b2c17cd5 (patch) | |
| tree | b59e6657f6350aefe8a67631d8398ba584a9717e /src | |
| parent | e9d1e5e533d12abe14582736d90e4cb173addc56 (diff) | |
| parent | 15a0b30d8e905a7b46fa97175d9bdba2bd5a8403 (diff) | |
| download | zig-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.zig | 102 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 98 | ||||
| -rw-r--r-- | src/codegen/llvm/bindings.zig | 15 | ||||
| -rw-r--r-- | src/value.zig | 26 |
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); |
