diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-03-21 20:05:29 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-03-21 20:05:29 -0400 |
| commit | 71413568389850e821df0784166d840de4d8f96e (patch) | |
| tree | ccc577b34bc44433c78a56c4ab7065ac5ee50a2d /src | |
| parent | 2f4473b6536ee43e51a17b02d8fad7518ab32c3b (diff) | |
| parent | 7eddef423d74318ef9190864232f2e224837461e (diff) | |
| download | zig-71413568389850e821df0784166d840de4d8f96e.tar.gz zig-71413568389850e821df0784166d840de4d8f96e.zip | |
Merge pull request #11237 from wsengir/stage2-vectors
stage2: implement most vector operations in Sema and LLVM backend
Diffstat (limited to 'src')
| -rw-r--r-- | src/Air.zig | 23 | ||||
| -rw-r--r-- | src/Liveness.zig | 4 | ||||
| -rw-r--r-- | src/Sema.zig | 525 | ||||
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 6 | ||||
| -rw-r--r-- | src/arch/arm/CodeGen.zig | 7 | ||||
| -rw-r--r-- | src/arch/riscv64/CodeGen.zig | 6 | ||||
| -rw-r--r-- | src/arch/wasm/CodeGen.zig | 6 | ||||
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 6 | ||||
| -rw-r--r-- | src/codegen/c.zig | 2 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 144 | ||||
| -rw-r--r-- | src/print_air.zig | 15 | ||||
| -rw-r--r-- | src/value.zig | 769 |
12 files changed, 1188 insertions, 325 deletions
diff --git a/src/Air.zig b/src/Air.zig index 2d717f442d..e0f765ddc0 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -308,6 +308,10 @@ pub const Inst = struct { /// `!=`. Result type is always bool. /// Uses the `bin_op` field. cmp_neq, + /// Conditional between two vectors. + /// Result type is always a vector of bools. + /// Uses the `ty_pl` field, payload is `VectorCmp`. + cmp_vector, /// Conditional branch. /// Result type is always noreturn; no instructions in a block follow this one. @@ -781,6 +785,20 @@ pub const Shuffle = struct { mask_len: u32, }; +pub const VectorCmp = struct { + lhs: Inst.Ref, + rhs: Inst.Ref, + op: u32, + + pub fn compareOperator(self: VectorCmp) std.math.CompareOperator { + return @intToEnum(std.math.CompareOperator, @truncate(u3, self.op)); + } + + pub fn encodeOp(compare_operator: std.math.CompareOperator) u32 { + return @enumToInt(compare_operator); + } +}; + /// Trailing: /// 0. `Inst.Ref` for every outputs_len /// 1. `Inst.Ref` for every inputs_len @@ -886,6 +904,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .shl_sat, .min, .max, + .bool_and, + .bool_or, => return air.typeOf(datas[inst].bin_op.lhs), .sqrt, @@ -917,8 +937,6 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .is_non_err, .is_err_ptr, .is_non_err_ptr, - .bool_and, - .bool_or, => return Type.initTag(.bool), .const_ty => return Type.initTag(.type), @@ -942,6 +960,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .aggregate_init, .union_init, .field_parent_ptr, + .cmp_vector, => return air.getRefType(datas[inst].ty_pl.ty), .not, diff --git a/src/Liveness.zig b/src/Liveness.zig index dd93d44f72..79521e7a94 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -441,6 +441,10 @@ fn analyzeInst( const reduce = inst_datas[inst].reduce; return trackOperands(a, new_set, inst, main_tomb, .{ reduce.operand, .none, .none }); }, + .cmp_vector => { + const extra = a.air.extraData(Air.VectorCmp, inst_datas[inst].ty_pl.payload).data; + return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); + }, .aggregate_init => { const ty_pl = inst_datas[inst].ty_pl; const aggregate_ty = a.air.getRefType(ty_pl.ty); diff --git a/src/Sema.zig b/src/Sema.zig index 7674121ba6..208f33cf7c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -397,6 +397,20 @@ pub const Block = struct { }); } + fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator, vector_ty: Air.Inst.Ref) !Air.Inst.Ref { + return block.addInst(.{ + .tag = .cmp_vector, + .data = .{ .ty_pl = .{ + .ty = vector_ty, + .payload = try block.sema.addExtra(Air.VectorCmp{ + .lhs = lhs, + .rhs = rhs, + .op = Air.VectorCmp.encodeOp(cmp_op), + }), + } }, + }); + } + fn addAggregateInit( block: *Block, aggregate_ty: Type, @@ -2091,7 +2105,7 @@ fn zirEnumDecl( }); } else if (any_values) { const tag_val = if (last_tag_val) |val| - try val.intAdd(Value.one, sema.arena) + try val.intAdd(Value.one, enum_obj.tag_ty, sema.arena) else Value.zero; last_tag_val = tag_val; @@ -8178,14 +8192,22 @@ fn zirShl( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const lhs = sema.resolveInst(extra.lhs); const rhs = sema.resolveInst(extra.rhs); + const lhs_ty = sema.typeOf(lhs); + const rhs_ty = sema.typeOf(rhs); + const target = sema.mod.getTarget(); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + + const scalar_ty = lhs_ty.scalarType(); + const scalar_rhs_ty = rhs_ty.scalarType(); // TODO coerce rhs if air_tag is not shl_sat - const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, sema.typeOf(rhs)); + const rhs_is_comptime_int = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); @@ -8199,35 +8221,31 @@ fn zirShl( } } - const lhs_ty = sema.typeOf(lhs); - const rhs_ty = sema.typeOf(rhs); - const target = sema.mod.getTarget(); - const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); const rhs_val = maybe_rhs_val orelse break :rs rhs_src; const val = switch (air_tag) { .shl_exact => val: { - const shifted = try lhs_val.shl(rhs_val, sema.arena); - if (lhs_ty.zigTypeTag() == .ComptimeInt) { + const shifted = try lhs_val.shl(rhs_val, lhs_ty, sema.arena); + if (scalar_ty.zigTypeTag() == .ComptimeInt) { break :val shifted; } - const int_info = lhs_ty.intInfo(target); - const truncated = try shifted.intTrunc(sema.arena, int_info.signedness, int_info.bits); - if (truncated.compareHetero(.eq, shifted)) { + const int_info = scalar_ty.intInfo(target); + const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits); + if (truncated.compare(.eq, shifted, lhs_ty)) { break :val shifted; } return sema.addConstUndef(lhs_ty); }, - .shl_sat => if (lhs_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, sema.arena) + .shl_sat => if (scalar_ty.zigTypeTag() == .ComptimeInt) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena) else try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, target), - .shl => if (lhs_ty.zigTypeTag() == .ComptimeInt) - try lhs_val.shl(rhs_val, sema.arena) + .shl => if (scalar_ty.zigTypeTag() == .ComptimeInt) + try lhs_val.shl(rhs_val, lhs_ty, sema.arena) else try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, target), @@ -8242,7 +8260,7 @@ fn zirShl( const new_rhs = if (air_tag == .shl_sat) rhs: { // Limit the RHS type for saturating shl to be an integer as small as the LHS. if (rhs_is_comptime_int or - rhs_ty.intInfo(target).bits > lhs_ty.intInfo(target).bits) + scalar_rhs_ty.intInfo(target).bits > scalar_ty.intInfo(target).bits) { const max_int = try sema.addConstant( lhs_ty, @@ -8269,15 +8287,18 @@ fn zirShr( defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; const lhs = sema.resolveInst(extra.lhs); const rhs = sema.resolveInst(extra.rhs); + const lhs_ty = sema.typeOf(lhs); + const rhs_ty = sema.typeOf(rhs); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const runtime_src = if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| rs: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { - const lhs_ty = sema.typeOf(lhs); if (lhs_val.isUndef() or rhs_val.isUndef()) { return sema.addConstUndef(lhs_ty); } @@ -8287,13 +8308,12 @@ fn zirShr( } if (air_tag == .shr_exact) { // Detect if any ones would be shifted out. - const bits = @intCast(u16, rhs_val.toUnsignedInt()); - const truncated = try lhs_val.intTrunc(sema.arena, .unsigned, bits); + const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val); if (!truncated.compareWithZero(.eq)) { return sema.addConstUndef(lhs_ty); } } - const val = try lhs_val.shr(rhs_val, sema.arena); + const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena); return sema.addConstant(lhs_ty, val); } else { // Even if lhs is not comptime known, we can still deduce certain things based @@ -8328,32 +8348,15 @@ fn zirBitwise( const rhs = sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); - const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); - const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - - const scalar_type = if (resolved_type.zigTypeTag() == .Vector) - resolved_type.elemType() - else - resolved_type; - + const scalar_type = resolved_type.scalarType(); const scalar_tag = scalar_type.zigTypeTag(); - if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) { - if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { - return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ - lhs_ty.arrayLen(), - rhs_ty.arrayLen(), - }); - } - } else if (lhs_ty.zigTypeTag() == .Vector or rhs_ty.zigTypeTag() == .Vector) { - return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ - lhs_ty, - rhs_ty, - }); - } + const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); + const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; @@ -8363,16 +8366,13 @@ fn zirBitwise( if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { - if (resolved_type.zigTypeTag() == .Vector) { - return sema.fail(block, src, "TODO implement zirBitwise for vectors at comptime", .{}); - } const result_val = switch (air_tag) { - .bit_and => try lhs_val.bitwiseAnd(rhs_val, sema.arena), - .bit_or => try lhs_val.bitwiseOr(rhs_val, sema.arena), - .xor => try lhs_val.bitwiseXor(rhs_val, sema.arena), + .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena), + .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena), + .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena), else => unreachable, }; - return sema.addConstant(scalar_type, result_val); + return sema.addConstant(resolved_type, result_val); } } @@ -8399,9 +8399,9 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const target = sema.mod.getTarget(); if (val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(operand_type); } else if (operand_type.zigTypeTag() == .Vector) { - const vec_len = try sema.usizeCast(block, operand_src, operand_type.arrayLen()); + const vec_len = try sema.usizeCast(block, operand_src, operand_type.vectorLen()); var elem_val_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, vec_len); for (elems) |*elem, i| { @@ -8413,8 +8413,8 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. try Value.Tag.aggregate.create(sema.arena, elems), ); } else { - const result_val = try val.bitwiseNot(scalar_type, sema.arena, target); - return sema.addConstant(scalar_type, result_val); + const result_val = try val.bitwiseNot(operand_type, sema.arena, target); + return sema.addConstant(operand_type, result_val); } } @@ -8766,8 +8766,19 @@ fn zirNegate( const src = inst_data.src(); const lhs_src = src; const rhs_src = src; // TODO better source location - const lhs = sema.resolveInst(.zero); + const rhs = sema.resolveInst(inst_data.operand); + const rhs_ty = sema.typeOf(rhs); + const rhs_scalar_ty = rhs_ty.scalarType(); + + if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) { + return sema.fail(block, src, "negation of type '{}'", .{rhs_ty}); + } + + const lhs = if (rhs_ty.zigTypeTag() == .Vector) + try sema.addConstant(rhs_ty, try Value.Tag.repeated.create(sema.arena, Value.zero)) + else + sema.resolveInst(.zero); return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); } @@ -8985,18 +8996,8 @@ fn analyzeArithmetic( const rhs_ty = sema.typeOf(rhs); const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); - if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) { - if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) { - return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ - lhs_ty.arrayLen(), rhs_ty.arrayLen(), - }); - } - return sema.fail(block, src, "TODO implement support for vectors in Sema.analyzeArithmetic", .{}); - } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { - return sema.fail(block, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ - lhs_ty, rhs_ty, - }); - } + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + if (lhs_zig_ty_tag == .Pointer) switch (lhs_ty.ptrSize()) { .One, .Slice => {}, .Many, .C => { @@ -9019,15 +9020,13 @@ fn analyzeArithmetic( const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, }); + const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); - const scalar_type = if (resolved_type.zigTypeTag() == .Vector) - resolved_type.elemType() - else - resolved_type; - - const scalar_tag = scalar_type.zigTypeTag(); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const scalar_tag = resolved_type.scalarType().zigTypeTag(); const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; @@ -9061,7 +9060,7 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (rhs_val.compareWithZero(.eq)) { @@ -9073,19 +9072,19 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intAdd(rhs_val, sema.arena), + resolved_type, + try lhs_val.intAdd(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatAdd(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .add }; @@ -9102,15 +9101,15 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.numberAddWrap(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = lhs_src, .air_tag = .addwrap }; } else break :rs .{ .src = rhs_src, .air_tag = .addwrap }; @@ -9126,18 +9125,18 @@ fn analyzeArithmetic( } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intAdd(rhs_val, sema.arena) + try lhs_val.intAdd(rhs_val, resolved_type, sema.arena) else - try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target); + try lhs_val.intAddSat(rhs_val, resolved_type, sema.arena, target); - return sema.addConstant(scalar_type, val); + return sema.addConstant(resolved_type, val); } else break :rs .{ .src = lhs_src, .air_tag = .add_sat }; } else break :rs .{ .src = rhs_src, .air_tag = .add_sat }; }, @@ -9154,7 +9153,7 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (rhs_val.compareWithZero(.eq)) { @@ -9166,19 +9165,19 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intSub(rhs_val, sema.arena), + resolved_type, + try lhs_val.intSub(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatSub(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatSub(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .sub }; @@ -9190,7 +9189,7 @@ fn analyzeArithmetic( // If either of the operands are undefined, the result is undefined. if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; @@ -9198,12 +9197,12 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.numberSubWrap(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.numberSubWrap(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .subwrap }; } else break :rs .{ .src = lhs_src, .air_tag = .subwrap }; @@ -9214,7 +9213,7 @@ fn analyzeArithmetic( // If either of the operands are undefined, result is undefined. if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { return casted_lhs; @@ -9222,15 +9221,15 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intSub(rhs_val, sema.arena) + try lhs_val.intSub(rhs_val, resolved_type, sema.arena) else - try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target); + try lhs_val.intSubSat(rhs_val, resolved_type, sema.arena, target); - return sema.addConstant(scalar_type, val); + return sema.addConstant(resolved_type, val); } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat }; } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; }, @@ -9260,7 +9259,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9274,27 +9273,27 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { - return sema.addConstUndef(scalar_type); + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + return sema.addConstUndef(resolved_type); } } return sema.failWithUseOfUndef(block, rhs_src); } - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intDiv(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), ); } } else { @@ -9335,7 +9334,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9349,27 +9348,27 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { - return sema.addConstUndef(scalar_type); + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + return sema.addConstUndef(resolved_type); } } return sema.failWithUseOfUndef(block, rhs_src); } - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intDiv(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; @@ -9398,7 +9397,7 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9412,27 +9411,27 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) { if (maybe_rhs_val) |rhs_val| { - if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { - return sema.addConstUndef(scalar_type); + if (rhs_val.compare(.neq, Value.negative_one, rhs_ty)) { + return sema.addConstUndef(resolved_type); } } return sema.failWithUseOfUndef(block, rhs_src); } - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intDivFloor(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_floor }; @@ -9460,7 +9459,7 @@ fn analyzeArithmetic( return sema.failWithUseOfUndef(block, rhs_src); } else { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } } } @@ -9477,14 +9476,14 @@ fn analyzeArithmetic( if (is_int) { // TODO: emit compile error if there is a remainder return sema.addConstant( - scalar_type, - try lhs_val.intDiv(rhs_val, sema.arena), + resolved_type, + try lhs_val.intDiv(rhs_val, resolved_type, sema.arena), ); } else { // TODO: emit compile error if there is a remainder return sema.addConstant( - scalar_type, - try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = rhs_src, .air_tag = .div_exact }; @@ -9502,9 +9501,9 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, scalar_type)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty)) { return casted_rhs; } } @@ -9514,13 +9513,13 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, rhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (rhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, scalar_type)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { @@ -9528,18 +9527,18 @@ fn analyzeArithmetic( if (is_int) { return sema.failWithUseOfUndef(block, lhs_src); } else { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } } if (is_int) { return sema.addConstant( - scalar_type, - try lhs_val.intMul(rhs_val, sema.arena), + resolved_type, + try lhs_val.intMul(rhs_val, resolved_type, sema.arena), ); } else { return sema.addConstant( - scalar_type, - try lhs_val.floatMul(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatMul(rhs_val, resolved_type, sema.arena, target), ); } } else break :rs .{ .src = lhs_src, .air_tag = .mul }; @@ -9553,30 +9552,30 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, scalar_type)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty)) { return casted_rhs; } } } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, scalar_type)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } return sema.addConstant( - scalar_type, - try lhs_val.numberMulWrap(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.numberMulWrap(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap }; } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap }; @@ -9589,34 +9588,34 @@ fn analyzeArithmetic( if (maybe_lhs_val) |lhs_val| { if (!lhs_val.isUndef()) { if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (lhs_val.compare(.eq, Value.one, scalar_type)) { + if (lhs_val.compare(.eq, Value.one, lhs_ty)) { return casted_rhs; } } } if (maybe_rhs_val) |rhs_val| { if (rhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (rhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - if (rhs_val.compare(.eq, Value.one, scalar_type)) { + if (rhs_val.compare(.eq, Value.one, rhs_ty)) { return casted_lhs; } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } const val = if (scalar_tag == .ComptimeInt) - try lhs_val.intMul(rhs_val, sema.arena) + try lhs_val.intMul(rhs_val, resolved_type, sema.arena) else - try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target); + try lhs_val.intMulSat(rhs_val, resolved_type, sema.arena, target); - return sema.addConstant(scalar_type, val); + return sema.addConstant(resolved_type, val); } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat }; } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat }; }, @@ -9640,9 +9639,9 @@ fn analyzeArithmetic( return sema.failWithUseOfUndef(block, lhs_src); } if (lhs_val.compareWithZero(.eq)) { - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - } else if (lhs_ty.isSignedInt()) { + } else if (lhs_scalar_ty.isSignedInt()) { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); } if (maybe_rhs_val) |rhs_val| { @@ -9653,7 +9652,7 @@ fn analyzeArithmetic( return sema.failWithDivideByZero(block, rhs_src); } if (maybe_lhs_val) |lhs_val| { - const rem_result = try lhs_val.intRem(rhs_val, sema.arena); + const rem_result = try lhs_val.intRem(rhs_val, resolved_type, sema.arena); // If this answer could possibly be different by doing `intMod`, // we must emit a compile error. Otherwise, it's OK. if (rhs_val.compareWithZero(.lt) != lhs_val.compareWithZero(.lt) and @@ -9667,12 +9666,12 @@ fn analyzeArithmetic( } if (lhs_val.compareWithZero(.lt)) { // Negative - return sema.addConstant(scalar_type, Value.zero); + return sema.addConstant(resolved_type, Value.zero); } - return sema.addConstant(scalar_type, rem_result); + return sema.addConstant(resolved_type, rem_result); } break :rs .{ .src = lhs_src, .air_tag = .rem }; - } else if (rhs_ty.isSignedInt()) { + } else if (rhs_scalar_ty.isSignedInt()) { return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); } else { break :rs .{ .src = rhs_src, .air_tag = .rem }; @@ -9694,8 +9693,8 @@ fn analyzeArithmetic( return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); } return sema.addConstant( - scalar_type, - try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target), ); } else { return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); @@ -9731,8 +9730,8 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.intRem(rhs_val, sema.arena), + resolved_type, + try lhs_val.intRem(rhs_val, resolved_type, sema.arena), ); } break :rs .{ .src = lhs_src, .air_tag = .rem }; @@ -9751,12 +9750,12 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.floatRem(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatRem(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .rem }; } else break :rs .{ .src = lhs_src, .air_tag = .rem }; @@ -9788,8 +9787,8 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.intMod(rhs_val, sema.arena), + resolved_type, + try lhs_val.intMod(rhs_val, resolved_type, sema.arena), ); } break :rs .{ .src = lhs_src, .air_tag = .mod }; @@ -9808,12 +9807,12 @@ fn analyzeArithmetic( } if (maybe_lhs_val) |lhs_val| { if (lhs_val.isUndef()) { - return sema.addConstUndef(scalar_type); + return sema.addConstUndef(resolved_type); } if (maybe_rhs_val) |rhs_val| { return sema.addConstant( - scalar_type, - try lhs_val.floatMod(rhs_val, scalar_type, sema.arena, target), + resolved_type, + try lhs_val.floatMod(rhs_val, resolved_type, sema.arena, target), ); } else break :rs .{ .src = rhs_src, .air_tag = .mod }; } else break :rs .{ .src = lhs_src, .air_tag = .mod }; @@ -10164,6 +10163,11 @@ fn analyzeCmp( ) CompileError!Air.Inst.Ref { const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + + if (lhs_ty.zigTypeTag() == .Vector and rhs_ty.zigTypeTag() == .Vector) { + return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); + } if (lhs_ty.isNumeric() and rhs_ty.isNumeric()) { // This operation allows any combination of integer and float types, regardless of the // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for @@ -10198,6 +10202,12 @@ fn cmpSelf( if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); + if (resolved_type.zigTypeTag() == .Vector) { + const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); + const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena); + return sema.addConstant(result_ty, cmp_val); + } + if (lhs_val.compare(op, rhs_val, resolved_type)) { return Air.Inst.Ref.bool_true; } else { @@ -10223,16 +10233,12 @@ fn cmpSelf( } }; try sema.requireRuntimeBlock(block, runtime_src); - - const tag: Air.Inst.Tag = switch (op) { - .lt => .cmp_lt, - .lte => .cmp_lte, - .eq => .cmp_eq, - .gte => .cmp_gte, - .gt => .cmp_gt, - .neq => .cmp_neq, - }; - // TODO handle vectors + if (resolved_type.zigTypeTag() == .Vector) { + const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool"); + const result_ty_ref = try sema.addType(result_ty); + return block.addCmpVector(casted_lhs, casted_rhs, op, result_ty_ref); + } + const tag = Air.Inst.Tag.fromCmpOp(op); return block.addBinOp(tag, casted_lhs, casted_rhs); } @@ -11353,7 +11359,7 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi const elem_ty = operand.elemType2(); const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); return Type.Tag.vector.create(sema.arena, .{ - .len = operand.arrayLen(), + .len = operand.vectorLen(), .elem_type = log2_elem_ty, }); }, @@ -13284,7 +13290,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const target = sema.mod.getTarget(); - const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { + const result_val = val.floatToInt(sema.arena, operand_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); }, @@ -13311,7 +13317,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { const target = sema.mod.getTarget(); - const result_val = try val.intToFloat(sema.arena, dest_ty, target); + const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target); return sema.addConstant(dest_ty, result_val); } @@ -13521,14 +13527,14 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai if (!is_vector) { return sema.addConstant( dest_ty, - try val.intTrunc(sema.arena, dest_info.signedness, dest_info.bits), + try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits), ); } var elem_buf: Value.ElemValueBuffer = undefined; const elems = try sema.arena.alloc(Value, operand_ty.vectorLen()); for (elems) |*elem, i| { const elem_val = val.elemValueBuffer(i, &elem_buf); - elem.* = try elem_val.intTrunc(sema.arena, dest_info.signedness, dest_info.bits); + elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits); } return sema.addConstant( dest_ty, @@ -14083,13 +14089,40 @@ fn checkSimdBinOp( ) CompileError!SimdBinOp { const lhs_ty = sema.typeOf(uncasted_lhs); const rhs_ty = sema.typeOf(uncasted_rhs); + + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + var vec_len: ?usize = if (lhs_ty.zigTypeTag() == .Vector) lhs_ty.vectorLen() else null; + const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ + .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, + }); + const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); + const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); + + return SimdBinOp{ + .len = vec_len, + .lhs = lhs, + .rhs = rhs, + .lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs), + .rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs), + .result_ty = result_ty, + .scalar_ty = result_ty.scalarType(), + }; +} + +fn checkVectorizableBinaryOperands( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs_ty: Type, + rhs_ty: Type, + lhs_src: LazySrcLoc, + rhs_src: LazySrcLoc, +) CompileError!void { const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison(); const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison(); - - var vec_len: ?usize = null; if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) { - const lhs_len = lhs_ty.arrayLen(); - const rhs_len = rhs_ty.arrayLen(); + const lhs_len = lhs_ty.vectorLen(); + const rhs_len = rhs_ty.vectorLen(); if (lhs_len != rhs_len) { const msg = msg: { const msg = try sema.errMsg(block, src, "vector length mismatch", .{}); @@ -14100,7 +14133,6 @@ fn checkSimdBinOp( }; return sema.failWithOwnedErrorMsg(block, msg); } - vec_len = try sema.usizeCast(block, lhs_src, lhs_len); } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { const msg = msg: { const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{ @@ -14118,21 +14150,6 @@ fn checkSimdBinOp( }; return sema.failWithOwnedErrorMsg(block, msg); } - const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ - .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, - }); - const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); - const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); - - return SimdBinOp{ - .len = vec_len, - .lhs = lhs, - .rhs = rhs, - .lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs), - .rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs), - .result_ty = result_ty, - .scalar_ty = result_ty.scalarType(), - }; } fn resolveExportOptions( @@ -14362,9 +14379,9 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. while (i < vec_len) : (i += 1) { const elem_val = operand_val.elemValueBuffer(i, &elem_buf); switch (operation) { - .And => accum = try accum.bitwiseAnd(elem_val, sema.arena), - .Or => accum = try accum.bitwiseOr(elem_val, sema.arena), - .Xor => accum = try accum.bitwiseXor(elem_val, sema.arena), + .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena), + .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena), + .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena), .Min => accum = accum.numberMin(elem_val), .Max => accum = accum.numberMax(elem_val), .Add => accum = try accum.numberAddWrap(elem_val, scalar_ty, sema.arena, target), @@ -14683,10 +14700,10 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A .Xchg => operand_val, .Add => try stored_val.numberAddWrap(operand_val, operand_ty, sema.arena, target), .Sub => try stored_val.numberSubWrap(operand_val, operand_ty, sema.arena, target), - .And => try stored_val.bitwiseAnd (operand_val, sema.arena), + .And => try stored_val.bitwiseAnd (operand_val, operand_ty, sema.arena), .Nand => try stored_val.bitwiseNand (operand_val, operand_ty, sema.arena, target), - .Or => try stored_val.bitwiseOr (operand_val, sema.arena), - .Xor => try stored_val.bitwiseXor (operand_val, sema.arena), + .Or => try stored_val.bitwiseOr (operand_val, operand_ty, sema.arena), + .Xor => try stored_val.bitwiseXor (operand_val, operand_ty, sema.arena), .Max => stored_val.numberMax (operand_val), .Min => stored_val.numberMin (operand_val), // zig fmt: on @@ -17509,7 +17526,7 @@ fn coerce( if (val.floatHasFraction()) { return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty), dest_ty }); } - const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { + const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) { error.FloatCannotFit => { return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); }, @@ -17572,7 +17589,7 @@ fn coerce( }, .Int, .ComptimeInt => int: { const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int; - const result_val = try val.intToFloat(sema.arena, dest_ty, target); + const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target); // TODO implement this compile error //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty); //if (!int_again_val.eql(val, inst_ty)) { @@ -17809,8 +17826,21 @@ fn coerceInMemoryAllowed( return .ok; } + // Vectors + if (dest_tag == .Vector and src_tag == .Vector) vectors: { + const dest_len = dest_ty.vectorLen(); + const src_len = src_ty.vectorLen(); + if (dest_len != src_len) break :vectors; + + const dest_elem_ty = dest_ty.scalarType(); + const src_elem_ty = src_ty.scalarType(); + const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); + if (child == .no_match) break :vectors; + + return .ok; + } + // TODO: non-pointer-like optionals - // TODO: vectors return .no_match; } @@ -19683,19 +19713,6 @@ fn cmpNumeric( const lhs_ty_tag = lhs_ty.zigTypeTag(); const rhs_ty_tag = rhs_ty.zigTypeTag(); - if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { - if (lhs_ty.vectorLen() != rhs_ty.vectorLen()) { - return sema.fail(block, src, "vector length mismatch: {d} and {d}", .{ - lhs_ty.vectorLen(), rhs_ty.vectorLen(), - }); - } - return sema.fail(block, src, "TODO implement support for vectors in cmpNumeric", .{}); - } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { - return sema.fail(block, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ - lhs_ty, rhs_ty, - }); - } - const runtime_src: LazySrcLoc = src: { if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { @@ -19881,6 +19898,46 @@ fn cmpNumeric( return block.addBinOp(Air.Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); } +/// Asserts that lhs and rhs types are both vectors. +fn cmpVector( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + lhs: Air.Inst.Ref, + rhs: Air.Inst.Ref, + op: std.math.CompareOperator, + lhs_src: LazySrcLoc, + rhs_src: LazySrcLoc, +) CompileError!Air.Inst.Ref { + const lhs_ty = sema.typeOf(lhs); + const rhs_ty = sema.typeOf(rhs); + assert(lhs_ty.zigTypeTag() == .Vector); + assert(rhs_ty.zigTypeTag() == .Vector); + try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); + + const result_ty = try Type.vector(sema.arena, lhs_ty.vectorLen(), Type.@"bool"); + + const runtime_src: LazySrcLoc = src: { + if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { + if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { + if (lhs_val.isUndef() or rhs_val.isUndef()) { + return sema.addConstUndef(result_ty); + } + const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena); + return sema.addConstant(result_ty, cmp_val); + } else { + break :src rhs_src; + } + } else { + break :src lhs_src; + } + }; + + try sema.requireRuntimeBlock(block, runtime_src); + const result_ty_inst = try sema.addType(result_ty); + return block.addCmpVector(lhs, rhs, op, result_ty_inst); +} + fn wrapOptional( sema: *Sema, block: *Block, @@ -21187,7 +21244,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty }); } else { const val = if (last_tag_val) |val| - try val.intAdd(Value.one, sema.arena) + try val.intAdd(Value.one, int_tag_ty, sema.arena) else Value.zero; last_tag_val = val; diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index c9121c8859..9c2d5890a7 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -577,6 +577,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBinOp(inst), .bool_or => try self.airBinOp(inst), @@ -2713,6 +2714,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 921bc92c72..0b9f9eb2e2 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -567,6 +567,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBinOp(inst), .bool_or => try self.airBinOp(inst), @@ -2894,7 +2895,6 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const lhs_ty = self.air.typeOf(bin_op.lhs); switch (lhs_ty.zigTypeTag()) { - .Vector => return self.fail("TODO ARM cmp vectors", .{}), .Optional => return self.fail("TODO ARM cmp optionals", .{}), .Float => return self.fail("TODO ARM cmp floats", .{}), .Int, .Bool, .Pointer, .ErrorSet, .Enum => { @@ -2929,6 +2929,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 14beedd2a9..3b73fef51f 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -537,6 +537,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBoolOp(inst), .bool_or => try self.airBoolOp(inst), @@ -1791,6 +1792,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c60c321572..8d515e9365 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1309,6 +1309,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .cmp_lte => self.airCmp(inst, .lte), .cmp_lt => self.airCmp(inst, .lt), .cmp_neq => self.airCmp(inst, .neq), + .cmp_vector => self.airCmpVector(inst), .array_elem_val => self.airArrayElemVal(inst), .array_to_slice => self.airArrayToSlice(inst), @@ -2222,6 +2223,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner return cmp_tmp; } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) InnerError!WValue { + _ = inst; + return self.fail("TODO implement airCmpVector for wasm", .{}); +} + fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { const br = self.air.instructions.items(.data)[inst].br; const block = self.blocks.get(br.block_inst).?; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 72f12cf4e9..598511a631 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -658,6 +658,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .cmp_gte => try self.airCmp(inst, .gte), .cmp_gt => try self.airCmp(inst, .gt), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .bool_and => try self.airBoolOp(inst), .bool_or => try self.airBoolOp(inst), @@ -3699,6 +3700,11 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { + _ = inst; + return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); +} + fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; const payload = try self.addExtra(Mir.DbgLineColumn{ diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 23ccdc007b..f5a1036479 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1715,6 +1715,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .cmp_eq => try airEquality(f, inst, "((", "=="), .cmp_neq => try airEquality(f, inst, "!((", "!="), + .cmp_vector => return f.fail("TODO: C backend: implement binary op for tag '{s}'", .{@tagName(Air.Inst.Tag.cmp_vector)}), + // bool_and and bool_or are non-short-circuit operations .bool_and => try airBinOp(f, inst, " & "), .bool_or => try airBinOp(f, inst, " | "), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 139c1f25cb..107b059765 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3375,6 +3375,7 @@ pub const FuncGen = struct { .cmp_lt => try self.airCmp(inst, .lt), .cmp_lte => try self.airCmp(inst, .lte), .cmp_neq => try self.airCmp(inst, .neq), + .cmp_vector => try self.airCmpVector(inst), .is_non_null => try self.airIsNonNull(inst, false, false, .NE), .is_non_null_ptr => try self.airIsNonNull(inst, true , false, .NE), @@ -3640,6 +3641,20 @@ pub const FuncGen = struct { return self.cmp(lhs, rhs, operand_ty, op); } + fn airCmpVector(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; + + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const vec_ty = self.air.typeOf(extra.lhs); + const cmp_op = extra.compareOperator(); + + return self.cmp(lhs, rhs, vec_ty, cmp_op); + } + fn cmp( self: *FuncGen, lhs: *const llvm.Value, @@ -3650,9 +3665,10 @@ pub const FuncGen = struct { var int_buffer: Type.Payload.Bits = undefined; var opt_buffer: Type.Payload.ElemType = undefined; - const int_ty = switch (operand_ty.zigTypeTag()) { - .Enum => operand_ty.intTagType(&int_buffer), - .Int, .Bool, .Pointer, .ErrorSet => operand_ty, + const scalar_ty = operand_ty.scalarType(); + const int_ty = switch (scalar_ty.zigTypeTag()) { + .Enum => scalar_ty.intTagType(&int_buffer), + .Int, .Bool, .Pointer, .ErrorSet => scalar_ty, .Optional => blk: { const payload_ty = operand_ty.optionalChild(&opt_buffer); if (!payload_ty.hasRuntimeBitsIgnoreComptime() or operand_ty.isPtrLikeOptional()) { @@ -3944,10 +3960,11 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.air.typeOf(ty_op.operand); + const operand_scalar_ty = operand_ty.scalarType(); const dest_ty = self.air.typeOfIndex(inst); const dest_llvm_ty = try self.dg.llvmType(dest_ty); - if (operand_ty.isSignedInt()) { + if (operand_scalar_ty.isSignedInt()) { return self.builder.buildSIToFP(operand, dest_llvm_ty, ""); } else { return self.builder.buildUIToFP(operand, dest_llvm_ty, ""); @@ -3961,11 +3978,12 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[inst].ty_op; const operand = try self.resolveInst(ty_op.operand); const dest_ty = self.air.typeOfIndex(inst); + const dest_scalar_ty = dest_ty.scalarType(); const dest_llvm_ty = try self.dg.llvmType(dest_ty); // TODO set fast math flag - if (dest_ty.isSignedInt()) { + if (dest_scalar_ty.isSignedInt()) { return self.builder.buildFPToSI(operand, dest_llvm_ty, ""); } else { return self.builder.buildFPToUI(operand, dest_llvm_ty, ""); @@ -4896,9 +4914,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); return self.builder.buildNUWAdd(lhs, rhs, ""); } @@ -4919,9 +4938,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.todo("saturating float add", .{}); - if (inst_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.todo("saturating float add", .{}); + if (scalar_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, ""); return self.builder.buildUAddSat(lhs, rhs, ""); } @@ -4933,9 +4953,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); return self.builder.buildNUWSub(lhs, rhs, ""); } @@ -4956,9 +4977,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{}); - if (inst_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.todo("saturating float sub", .{}); + if (scalar_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, ""); return self.builder.buildUSubSat(lhs, rhs, ""); } @@ -4969,9 +4991,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); return self.builder.buildNUWMul(lhs, rhs, ""); } @@ -4992,9 +5015,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isAnyFloat()) return self.todo("saturating float mul", .{}); - if (inst_ty.isSignedInt()) return self.builder.buildSMulFixSat(lhs, rhs, ""); + if (scalar_ty.isAnyFloat()) return self.todo("saturating float mul", .{}); + if (scalar_ty.isSignedInt()) return self.builder.buildSMulFixSat(lhs, rhs, ""); return self.builder.buildUMulFixSat(lhs, rhs, ""); } @@ -5015,12 +5039,13 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) { + if (scalar_ty.isRuntimeFloat()) { const result = self.builder.buildFDiv(lhs, rhs, ""); return self.callTrunc(result, inst_ty); } - if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); return self.builder.buildUDiv(lhs, rhs, ""); } @@ -5031,12 +5056,13 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) { + if (scalar_ty.isRuntimeFloat()) { const result = self.builder.buildFDiv(lhs, rhs, ""); return try self.callFloor(result, inst_ty); } - if (inst_ty.isSignedInt()) { + if (scalar_ty.isSignedInt()) { // const d = @divTrunc(a, b); // const r = @rem(a, b); // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); @@ -5062,9 +5088,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); + if (scalar_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); return self.builder.buildExactUDiv(lhs, rhs, ""); } @@ -5075,9 +5102,10 @@ pub const FuncGen = struct { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); + if (scalar_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, ""); + if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); return self.builder.buildURem(lhs, rhs, ""); } @@ -5089,8 +5117,9 @@ pub const FuncGen = struct { const rhs = try self.resolveInst(bin_op.rhs); const inst_ty = self.air.typeOfIndex(inst); const inst_llvm_ty = try self.dg.llvmType(inst_ty); + const scalar_ty = inst_ty.scalarType(); - if (inst_ty.isRuntimeFloat()) { + if (scalar_ty.isRuntimeFloat()) { const a = self.builder.buildFRem(lhs, rhs, ""); const b = self.builder.buildFAdd(a, rhs, ""); const c = self.builder.buildFRem(b, rhs, ""); @@ -5098,7 +5127,7 @@ pub const FuncGen = struct { const ltz = self.builder.buildFCmp(.OLT, lhs, zero, ""); return self.builder.buildSelect(ltz, c, a, ""); } - if (inst_ty.isSignedInt()) { + if (scalar_ty.isSignedInt()) { const a = self.builder.buildSRem(lhs, rhs, ""); const b = self.builder.buildNSWAdd(a, rhs, ""); const c = self.builder.buildSRem(b, rhs, ""); @@ -5323,15 +5352,22 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const lhs_type = self.air.typeOf(bin_op.lhs); + + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") else rhs; - if (lhs_type.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, ""); + if (lhs_scalar_ty.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, ""); return self.builder.buildNUWShl(lhs, casted_rhs, ""); } @@ -5339,11 +5375,18 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); + const lhs_type = self.air.typeOf(bin_op.lhs); + const rhs_type = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_type.scalarType(); + const rhs_scalar_ty = rhs_type.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") else rhs; @@ -5354,31 +5397,45 @@ pub const FuncGen = struct { if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const lhs_type = self.air.typeOf(bin_op.lhs); + + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") else rhs; - if (lhs_type.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, ""); + if (lhs_scalar_ty.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, ""); return self.builder.buildUShlSat(lhs, casted_rhs, ""); } fn airShr(self: *FuncGen, inst: Air.Inst.Index, is_exact: bool) !?*const llvm.Value { - if (self.liveness.isUnused(inst)) - return null; + if (self.liveness.isUnused(inst)) return null; + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); - const lhs_type = self.air.typeOf(bin_op.lhs); + + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + const lhs_scalar_ty = lhs_ty.scalarType(); + const rhs_scalar_ty = rhs_ty.scalarType(); + const tg = self.dg.module.getTarget(); - const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg)) - self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "") + + const casted_rhs = if (rhs_scalar_ty.bitSize(tg) < lhs_scalar_ty.bitSize(tg)) + self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_ty), "") else rhs; - const is_signed_int = self.air.typeOfIndex(inst).isSignedInt(); + const is_signed_int = lhs_scalar_ty.isSignedInt(); if (is_exact) { if (is_signed_int) { @@ -5506,7 +5563,8 @@ pub const FuncGen = struct { if (bitcast_ok) { const llvm_vector_ty = try self.dg.llvmType(operand_ty); const casted_ptr = self.builder.buildBitCast(array_ptr, llvm_vector_ty.pointerType(0), ""); - _ = self.builder.buildStore(operand, casted_ptr); + const llvm_store = self.builder.buildStore(operand, casted_ptr); + llvm_store.setAlignment(inst_ty.abiAlignment(target)); } 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. diff --git a/src/print_air.zig b/src/print_air.zig index fb970e3b08..c6b27a6cd6 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -266,6 +266,7 @@ const Writer = struct { .mul_add => try w.writeMulAdd(s, inst), .shuffle => try w.writeShuffle(s, inst), .reduce => try w.writeReduce(s, inst), + .cmp_vector => try w.writeCmpVector(s, inst), .add_with_overflow, .sub_with_overflow, @@ -402,6 +403,16 @@ const Writer = struct { try s.print(", {s}", .{@tagName(reduce.operation)}); } + fn writeCmpVector(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.VectorCmp, ty_pl.payload).data; + + try s.print("{s}, ", .{@tagName(extra.compareOperator())}); + try w.writeOperand(s, inst, 0, extra.lhs); + try s.writeAll(", "); + try w.writeOperand(s, inst, 1, extra.rhs); + } + fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { const atomic_order = w.air.instructions.items(.data)[inst].fence; @@ -470,8 +481,8 @@ const Writer = struct { } fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const pl_op = w.air.instructions.items(.data)[inst].ty_pl; - const extra = w.air.extraData(Air.FieldParentPtr, pl_op.payload).data; + const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; try w.writeOperand(s, inst, 0, extra.field_ptr); try s.print(", {d}", .{extra.field_index}); diff --git a/src/value.zig b/src/value.zig index 76ab20c7ab..454b1d76a2 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1846,8 +1846,23 @@ pub const Value = extern union { return order(lhs, rhs).compare(op); } - /// Asserts the value is comparable. Both operands have type `ty`. + /// Asserts the values are comparable. Both operands have type `ty`. + /// Vector results will be reduced with AND. pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type) bool { + if (ty.zigTypeTag() == .Vector) { + var i: usize = 0; + while (i < ty.vectorLen()) : (i += 1) { + if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType())) { + return false; + } + } + return true; + } + return compareScalar(lhs, op, rhs, ty); + } + + /// Asserts the values are comparable. Both operands have type `ty`. + pub fn compareScalar(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type) bool { return switch (op) { .eq => lhs.eql(rhs, ty), .neq => !lhs.eql(rhs, ty), @@ -1855,18 +1870,25 @@ pub const Value = extern union { }; } + /// Asserts the values are comparable vectors of type `ty`. + pub fn compareVector(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, allocator: Allocator) !Value { + assert(ty.zigTypeTag() == .Vector); + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType()); + scalar.* = if (res_bool) Value.@"true" else Value.@"false"; + } + return Value.Tag.aggregate.create(allocator, result_data); + } + /// Asserts the value is comparable. - /// For vectors this is only valid with op == .eq. + /// Vector results will be reduced with AND. pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool { switch (lhs.tag()) { - .repeated => { - assert(op == .eq); - return lhs.castTag(.repeated).?.data.compareWithZero(.eq); - }, + .repeated => return lhs.castTag(.repeated).?.data.compareWithZero(op), .aggregate => { - assert(op == .eq); for (lhs.castTag(.aggregate).?.data) |elem_val| { - if (!elem_val.compareWithZero(.eq)) return false; + if (!elem_val.compareWithZero(op)) return false; } return true; }, @@ -2404,6 +2426,27 @@ pub const Value = extern union { }; } + /// Index into a vector-like `Value`. Asserts `index` is a valid index for `val`. + /// Some scalar values are considered vector-like to avoid needing to allocate + /// a new `repeated` each time a constant is used. + pub fn indexVectorlike(val: Value, index: usize) Value { + return switch (val.tag()) { + .aggregate => val.castTag(.aggregate).?.data[index], + + .repeated => val.castTag(.repeated).?.data, + // These values will implicitly be treated as `repeated`. + .zero, + .one, + .bool_false, + .bool_true, + .int_i64, + .int_u64, + => val, + + else => unreachable, + }; + } + /// Asserts the value is a single-item pointer to an array, or an array, /// or an unknown-length pointer, and returns the element value at the index. pub fn elemValue(val: Value, arena: Allocator, index: usize) !Value { @@ -2646,25 +2689,36 @@ pub const Value = extern union { }; } - pub fn intToFloat(val: Value, arena: Allocator, dest_ty: Type, target: Target) !Value { + pub fn intToFloat(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target) !Value { + if (int_ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, int_ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intToFloatScalar(val, arena, float_ty, target); + } + + pub fn intToFloatScalar(val: Value, arena: Allocator, float_ty: Type, target: Target) !Value { switch (val.tag()) { .undef, .zero, .one => return val, .the_only_possible_value => return Value.initTag(.zero), // for i0, u0 .int_u64 => { - return intToFloatInner(val.castTag(.int_u64).?.data, arena, dest_ty, target); + return intToFloatInner(val.castTag(.int_u64).?.data, arena, float_ty, target); }, .int_i64 => { - return intToFloatInner(val.castTag(.int_i64).?.data, arena, dest_ty, target); + return intToFloatInner(val.castTag(.int_i64).?.data, arena, float_ty, target); }, .int_big_positive => { const limbs = val.castTag(.int_big_positive).?.data; const float = bigIntToFloat(limbs, true); - return floatToValue(float, arena, dest_ty, target); + return floatToValue(float, arena, float_ty, target); }, .int_big_negative => { const limbs = val.castTag(.int_big_negative).?.data; const float = bigIntToFloat(limbs, false); - return floatToValue(float, arena, dest_ty, target); + return floatToValue(float, arena, float_ty, target); }, else => unreachable, } @@ -2694,7 +2748,18 @@ pub const Value = extern union { } } - pub fn floatToInt(val: Value, arena: Allocator, dest_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { + pub fn floatToInt(val: Value, arena: Allocator, float_ty: Type, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { + if (float_ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatToIntScalar(val.indexVectorlike(i), arena, int_ty.scalarType(), target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatToIntScalar(val, arena, int_ty, target); + } + + pub fn floatToIntScalar(val: Value, arena: Allocator, int_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { const Limb = std.math.big.Limb; var value = val.toFloat(f64); // TODO: f128 ? @@ -2724,7 +2789,7 @@ pub const Value = extern union { else try Value.Tag.int_big_positive.create(arena, result_limbs); - if (result.intFitsInType(dest_ty, target)) { + if (result.intFitsInType(int_ty, target)) { return result; } else { return error.FloatCannotFit; @@ -2771,7 +2836,7 @@ pub const Value = extern union { }; } - /// Supports both floats and ints; handles undefined. + /// Supports both (vectors of) floats and ints; handles undefined scalars. pub fn numberAddWrap( lhs: Value, rhs: Value, @@ -2779,10 +2844,28 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try numberAddWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return numberAddWrapScalar(lhs, rhs, ty, arena, target); + } + + /// Supports both floats and ints; handles undefined. + pub fn numberAddWrapScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intAdd(lhs, rhs, arena); + return intAdd(lhs, rhs, ty, arena); } if (ty.isAnyFloat()) { @@ -2809,7 +2892,7 @@ pub const Value = extern union { } } - /// Supports integers only; asserts neither operand is undefined. + /// Supports (vectors of) integers only; asserts neither operand is undefined. pub fn intAddSat( lhs: Value, rhs: Value, @@ -2817,6 +2900,24 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intAddSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intAddSatScalar(lhs, rhs, ty, arena, target); + } + + /// Supports integers only; asserts neither operand is undefined. + pub fn intAddSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { assert(!lhs.isUndef()); assert(!rhs.isUndef()); @@ -2861,7 +2962,7 @@ pub const Value = extern union { }; } - /// Supports both floats and ints; handles undefined. + /// Supports both (vectors of) floats and ints; handles undefined scalars. pub fn numberSubWrap( lhs: Value, rhs: Value, @@ -2869,10 +2970,28 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try numberSubWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return numberSubWrapScalar(lhs, rhs, ty, arena, target); + } + + /// Supports both floats and ints; handles undefined. + pub fn numberSubWrapScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intSub(lhs, rhs, arena); + return intSub(lhs, rhs, ty, arena); } if (ty.isAnyFloat()) { @@ -2883,7 +3002,7 @@ pub const Value = extern union { return overflow_result.wrapped_result; } - /// Supports integers only; asserts neither operand is undefined. + /// Supports (vectors of) integers only; asserts neither operand is undefined. pub fn intSubSat( lhs: Value, rhs: Value, @@ -2891,6 +3010,24 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intSubSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intSubSatScalar(lhs, rhs, ty, arena, target); + } + + /// Supports integers only; asserts neither operand is undefined. + pub fn intSubSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { assert(!lhs.isUndef()); assert(!rhs.isUndef()); @@ -2944,7 +3081,7 @@ pub const Value = extern union { }; } - /// Supports both floats and ints; handles undefined. + /// Supports both (vectors of) floats and ints; handles undefined scalars. pub fn numberMulWrap( lhs: Value, rhs: Value, @@ -2952,10 +3089,28 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try numberMulWrapScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return numberMulWrapScalar(lhs, rhs, ty, arena, target); + } + + /// Supports both floats and ints; handles undefined. + pub fn numberMulWrapScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); if (ty.zigTypeTag() == .ComptimeInt) { - return intMul(lhs, rhs, arena); + return intMul(lhs, rhs, ty, arena); } if (ty.isAnyFloat()) { @@ -2966,7 +3121,7 @@ pub const Value = extern union { return overflow_result.wrapped_result; } - /// Supports integers only; asserts neither operand is undefined. + /// Supports (vectors of) integers only; asserts neither operand is undefined. pub fn intMulSat( lhs: Value, rhs: Value, @@ -2974,6 +3129,24 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intMulSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return intMulSatScalar(lhs, rhs, ty, arena, target); + } + + /// Supports (vectors of) integers only; asserts neither operand is undefined. + pub fn intMulSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { assert(!lhs.isUndef()); assert(!rhs.isUndef()); @@ -3025,8 +3198,20 @@ pub const Value = extern union { }; } - /// operands must be integers; handles undefined. + /// operands must be (vectors of) integers; handles undefined scalars. pub fn bitwiseNot(val: Value, ty: Type, arena: Allocator, target: Target) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseNotScalar(val.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return bitwiseNotScalar(val, ty, arena, target); + } + + /// operands must be integers; handles undefined. + pub fn bitwiseNotScalar(val: Value, ty: Type, arena: Allocator, target: Target) !Value { if (val.isUndef()) return Value.initTag(.undef); const info = ty.intInfo(target); @@ -3050,8 +3235,20 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } + /// operands must be (vectors of) integers; handles undefined scalars. + pub fn bitwiseAnd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseAndScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return bitwiseAndScalar(lhs, rhs, allocator); + } + /// operands must be integers; handles undefined. - pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseAndScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without @@ -3070,22 +3267,46 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } - /// operands must be integers; handles undefined. + /// operands must be (vectors of) integers; handles undefined scalars. pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseNandScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return bitwiseNandScalar(lhs, rhs, ty, arena, target); + } + + /// operands must be integers; handles undefined. + pub fn bitwiseNandScalar(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); - const anded = try bitwiseAnd(lhs, rhs, arena); + const anded = try bitwiseAnd(lhs, rhs, ty, arena); const all_ones = if (ty.isSignedInt()) try Value.Tag.int_i64.create(arena, -1) else try ty.maxInt(arena, target); - return bitwiseXor(anded, all_ones, arena); + return bitwiseXor(anded, all_ones, ty, arena); + } + + /// operands must be (vectors of) integers; handles undefined scalars. + pub fn bitwiseOr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseOrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return bitwiseOrScalar(lhs, rhs, allocator); } /// operands must be integers; handles undefined. - pub fn bitwiseOr(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseOrScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without @@ -3103,8 +3324,20 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } + /// operands must be (vectors of) integers; handles undefined scalars. + pub fn bitwiseXor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try bitwiseXorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return bitwiseXorScalar(lhs, rhs, allocator); + } + /// operands must be integers; handles undefined. - pub fn bitwiseXor(lhs: Value, rhs: Value, arena: Allocator) !Value { + pub fn bitwiseXorScalar(lhs: Value, rhs: Value, arena: Allocator) !Value { if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); // TODO is this a performance issue? maybe we should try the operation without @@ -3123,7 +3356,18 @@ pub const Value = extern union { return fromBigInt(arena, result_bigint.toConst()); } - pub fn intAdd(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intAdd(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intAddScalar(lhs, rhs, allocator); + } + + pub fn intAddScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3139,7 +3383,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intSub(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intSub(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intSubScalar(lhs, rhs, allocator); + } + + pub fn intSubScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3155,7 +3410,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intDiv(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intDiv(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intDivScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intDivScalar(lhs, rhs, allocator); + } + + pub fn intDivScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3180,7 +3446,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_q.toConst()); } - pub fn intDivFloor(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intDivFloor(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intDivFloorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intDivFloorScalar(lhs, rhs, allocator); + } + + pub fn intDivFloorScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3205,7 +3482,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_q.toConst()); } - pub fn intRem(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intRem(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intRemScalar(lhs, rhs, allocator); + } + + pub fn intRemScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3232,7 +3520,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_r.toConst()); } - pub fn intMod(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intMod(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intModScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intModScalar(lhs, rhs, allocator); + } + + pub fn intModScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3270,6 +3569,17 @@ pub const Value = extern union { } pub fn floatRem(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatRemScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatRemScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatRemScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3304,6 +3614,17 @@ pub const Value = extern union { } pub fn floatMod(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatModScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatModScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatModScalar(lhs: Value, rhs: Value, float_type: Type, arena: Allocator, target: Target) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3337,7 +3658,18 @@ pub const Value = extern union { } } - pub fn intMul(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn intMul(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intMulScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intMulScalar(lhs, rhs, allocator); + } + + pub fn intMulScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3358,7 +3690,32 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn intTrunc(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + pub fn intTrunc(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, bits); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intTruncScalar(val, allocator, signedness, bits); + } + + /// This variant may vectorize on `bits`. Asserts that `bits` is a (vector of) `u16`. + pub fn intTruncBitsAsValue(val: Value, ty: Type, allocator: Allocator, signedness: std.builtin.Signedness, bits: Value) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try intTruncScalar(val.indexVectorlike(i), allocator, signedness, @intCast(u16, bits.indexVectorlike(i).toUnsignedInt())); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return intTruncScalar(val, allocator, signedness, @intCast(u16, bits.toUnsignedInt())); + } + + pub fn intTruncScalar(val: Value, allocator: Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { + if (bits == 0) return Value.zero; + var val_space: Value.BigIntSpace = undefined; const val_bigint = val.toBigInt(&val_space); @@ -3372,7 +3729,18 @@ pub const Value = extern union { return fromBigInt(allocator, result_bigint.toConst()); } - pub fn shl(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn shl(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shlScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return shlScalar(lhs, rhs, allocator); + } + + pub fn shlScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3429,6 +3797,23 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shlSatScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return shlSatScalar(lhs, rhs, ty, arena, target); + } + + pub fn shlSatScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. const info = ty.intInfo(target); @@ -3456,13 +3841,41 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { - const shifted = try lhs.shl(rhs, arena); + if (ty.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shlTruncScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), ty.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return shlTruncScalar(lhs, rhs, ty, arena, target); + } + + pub fn shlTruncScalar( + lhs: Value, + rhs: Value, + ty: Type, + arena: Allocator, + target: Target, + ) !Value { + const shifted = try lhs.shl(rhs, ty, arena); const int_info = ty.intInfo(target); - const truncated = try shifted.intTrunc(arena, int_info.signedness, int_info.bits); + const truncated = try shifted.intTrunc(ty, arena, int_info.signedness, int_info.bits); return truncated; } - pub fn shr(lhs: Value, rhs: Value, allocator: Allocator) !Value { + pub fn shr(lhs: Value, rhs: Value, ty: Type, allocator: Allocator) !Value { + if (ty.zigTypeTag() == .Vector) { + const result_data = try allocator.alloc(Value, ty.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try shrScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), allocator); + } + return Value.Tag.aggregate.create(allocator, result_data); + } + return shrScalar(lhs, rhs, allocator); + } + + pub fn shrScalar(lhs: Value, rhs: Value, allocator: Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -3496,6 +3909,23 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatAddScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatAddScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatAddScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3533,6 +3963,23 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatSubScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatSubScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatSubScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3570,6 +4017,23 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatDivScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatDivScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatDivScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3610,6 +4074,23 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatDivFloorScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatDivFloorScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatDivFloorScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3650,6 +4131,23 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatDivTruncScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatDivTruncScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatDivTruncScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3690,6 +4188,23 @@ pub const Value = extern union { arena: Allocator, target: Target, ) !Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floatMulScalar(lhs.indexVectorlike(i), rhs.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floatMulScalar(lhs, rhs, float_type, arena, target); + } + + pub fn floatMulScalar( + lhs: Value, + rhs: Value, + float_type: Type, + arena: Allocator, + target: Target, + ) !Value { switch (float_type.floatBits(target)) { 16 => { const lhs_val = lhs.toFloat(f16); @@ -3724,6 +4239,17 @@ pub const Value = extern union { } pub fn sqrt(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sqrtScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return sqrtScalar(val, float_type, arena, target); + } + + pub fn sqrtScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3756,6 +4282,17 @@ pub const Value = extern union { } pub fn sin(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try sinScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return sinScalar(val, float_type, arena, target); + } + + pub fn sinScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3788,6 +4325,17 @@ pub const Value = extern union { } pub fn cos(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try cosScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return cosScalar(val, float_type, arena, target); + } + + pub fn cosScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3820,6 +4368,17 @@ pub const Value = extern union { } pub fn exp(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try expScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return expScalar(val, float_type, arena, target); + } + + pub fn expScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3852,6 +4411,17 @@ pub const Value = extern union { } pub fn exp2(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try exp2Scalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return exp2Scalar(val, float_type, arena, target); + } + + pub fn exp2Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3884,6 +4454,17 @@ pub const Value = extern union { } pub fn log(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try logScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return logScalar(val, float_type, arena, target); + } + + pub fn logScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3916,6 +4497,17 @@ pub const Value = extern union { } pub fn log2(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try log2Scalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return log2Scalar(val, float_type, arena, target); + } + + pub fn log2Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3948,6 +4540,17 @@ pub const Value = extern union { } pub fn log10(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try log10Scalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return log10Scalar(val, float_type, arena, target); + } + + pub fn log10Scalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -3980,6 +4583,17 @@ pub const Value = extern union { } pub fn fabs(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try fabsScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return fabsScalar(val, float_type, arena, target); + } + + pub fn fabsScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4009,6 +4623,17 @@ pub const Value = extern union { } pub fn floor(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try floorScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return floorScalar(val, float_type, arena, target); + } + + pub fn floorScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4038,6 +4663,17 @@ pub const Value = extern union { } pub fn ceil(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try ceilScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return ceilScalar(val, float_type, arena, target); + } + + pub fn ceilScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4067,6 +4703,17 @@ pub const Value = extern union { } pub fn round(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try roundScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return roundScalar(val, float_type, arena, target); + } + + pub fn roundScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4096,6 +4743,17 @@ pub const Value = extern union { } pub fn trunc(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try truncScalar(val.indexVectorlike(i), float_type.scalarType(), arena, target); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return truncScalar(val, float_type, arena, target); + } + + pub fn truncScalar(val: Value, float_type: Type, arena: Allocator, target: Target) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const f = val.toFloat(f16); @@ -4132,6 +4790,31 @@ pub const Value = extern union { arena: Allocator, target: Target, ) Allocator.Error!Value { + if (float_type.zigTypeTag() == .Vector) { + const result_data = try arena.alloc(Value, float_type.vectorLen()); + for (result_data) |*scalar, i| { + scalar.* = try mulAddScalar( + float_type.scalarType(), + mulend1.indexVectorlike(i), + mulend2.indexVectorlike(i), + addend.indexVectorlike(i), + arena, + target, + ); + } + return Value.Tag.aggregate.create(arena, result_data); + } + return mulAddScalar(float_type, mulend1, mulend2, addend, arena, target); + } + + pub fn mulAddScalar( + float_type: Type, + mulend1: Value, + mulend2: Value, + addend: Value, + arena: Allocator, + target: Target, + ) Allocator.Error!Value { switch (float_type.floatBits(target)) { 16 => { const m1 = mulend1.toFloat(f16); |
