Added support for vector wrapping mult and sub
* I also merged the code that generates ir for add, sub, and multmaster
parent
06be65a602
commit
4010f6a11d
122
src/codegen.cpp
122
src/codegen.cpp
|
@ -2582,6 +2582,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
|
||||||
|
|
||||||
static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||||
IrInstructionBinOp *bin_op_instruction)
|
IrInstructionBinOp *bin_op_instruction)
|
||||||
{
|
{
|
||||||
|
@ -2640,50 +2642,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||||
} else {
|
} else {
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
case IrBinOpMult:
|
||||||
|
case IrBinOpMultWrap:
|
||||||
case IrBinOpAdd:
|
case IrBinOpAdd:
|
||||||
case IrBinOpAddWrap:
|
case IrBinOpAddWrap:
|
||||||
|
case IrBinOpSub:
|
||||||
|
case IrBinOpSubWrap: {
|
||||||
|
// These are lookup table using the AddSubMul enum as the lookup.
|
||||||
|
// If AddSubMul ever changes, then these tables will be out of
|
||||||
|
// date.
|
||||||
|
static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
|
||||||
|
static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
|
||||||
|
static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
|
||||||
|
static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
|
||||||
|
|
||||||
|
bool is_vector = type_entry->id == ZigTypeIdVector;
|
||||||
|
bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
|
||||||
|
AddSubMul add_sub_mul =
|
||||||
|
op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
|
||||||
|
op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
|
||||||
|
AddSubMulMul;
|
||||||
|
|
||||||
|
// The code that is generated for vectors and scalars are the same,
|
||||||
|
// so we can just set type_entry to the vectors elem_type an avoid
|
||||||
|
// a lot of repeated code.
|
||||||
|
if (is_vector)
|
||||||
|
type_entry = type_entry->data.vector.elem_type;
|
||||||
|
|
||||||
if (type_entry->id == ZigTypeIdPointer) {
|
if (type_entry->id == ZigTypeIdPointer) {
|
||||||
assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
|
assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
|
||||||
|
LLVMValueRef subscript_value;
|
||||||
|
if (is_vector)
|
||||||
|
zig_panic("TODO: Implement vector operations on pointers.");
|
||||||
|
|
||||||
|
switch (add_sub_mul) {
|
||||||
|
case AddSubMulAdd:
|
||||||
|
subscript_value = op2_value;
|
||||||
|
break;
|
||||||
|
case AddSubMulSub:
|
||||||
|
subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
|
||||||
|
break;
|
||||||
|
case AddSubMulMul:
|
||||||
|
zig_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
// TODO runtime safety
|
// TODO runtime safety
|
||||||
return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, "");
|
return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
|
||||||
} else if (type_entry->id == ZigTypeIdFloat) {
|
} else if (type_entry->id == ZigTypeIdFloat) {
|
||||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
||||||
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
|
return float_op[add_sub_mul](g->builder, op1_value, op2_value, "");
|
||||||
} else if (type_entry->id == ZigTypeIdInt) {
|
} else if (type_entry->id == ZigTypeIdInt) {
|
||||||
bool is_wrapping = (op_id == IrBinOpAddWrap);
|
|
||||||
if (is_wrapping) {
|
if (is_wrapping) {
|
||||||
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
|
return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
|
||||||
} else if (want_runtime_safety) {
|
} else if (want_runtime_safety) {
|
||||||
return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value);
|
if (is_vector)
|
||||||
|
zig_panic("TODO: Implement runtime safety vector operations.");
|
||||||
|
return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value);
|
||||||
} else if (type_entry->data.integral.is_signed) {
|
} else if (type_entry->data.integral.is_signed) {
|
||||||
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
|
return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
|
||||||
} else {
|
} else {
|
||||||
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
|
return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, "");
|
||||||
}
|
|
||||||
} else if (type_entry->id == ZigTypeIdVector) {
|
|
||||||
ZigType *elem_type = type_entry->data.vector.elem_type;
|
|
||||||
if (elem_type->id == ZigTypeIdFloat) {
|
|
||||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
|
||||||
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
|
|
||||||
} else if (elem_type->id == ZigTypeIdPointer) {
|
|
||||||
zig_panic("TODO codegen for pointers in vectors");
|
|
||||||
} else if (elem_type->id == ZigTypeIdInt) {
|
|
||||||
bool is_wrapping = (op_id == IrBinOpAddWrap);
|
|
||||||
if (is_wrapping) {
|
|
||||||
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
|
|
||||||
} else if (want_runtime_safety) {
|
|
||||||
zig_panic("TODO runtime safety for vector integer addition");
|
|
||||||
} else if (elem_type->data.integral.is_signed) {
|
|
||||||
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
|
|
||||||
} else {
|
|
||||||
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
zig_unreachable();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
case IrBinOpBinOr:
|
case IrBinOpBinOr:
|
||||||
return LLVMBuildOr(g->builder, op1_value, op2_value, "");
|
return LLVMBuildOr(g->builder, op1_value, op2_value, "");
|
||||||
case IrBinOpBinXor:
|
case IrBinOpBinXor:
|
||||||
|
@ -2728,49 +2751,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
|
||||||
return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
|
return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case IrBinOpSub:
|
|
||||||
case IrBinOpSubWrap:
|
|
||||||
if (type_entry->id == ZigTypeIdPointer) {
|
|
||||||
assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
|
|
||||||
// TODO runtime safety
|
|
||||||
LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
|
|
||||||
return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
|
|
||||||
} else if (type_entry->id == ZigTypeIdFloat) {
|
|
||||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
|
||||||
return LLVMBuildFSub(g->builder, op1_value, op2_value, "");
|
|
||||||
} else if (type_entry->id == ZigTypeIdInt) {
|
|
||||||
bool is_wrapping = (op_id == IrBinOpSubWrap);
|
|
||||||
if (is_wrapping) {
|
|
||||||
return LLVMBuildSub(g->builder, op1_value, op2_value, "");
|
|
||||||
} else if (want_runtime_safety) {
|
|
||||||
return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value);
|
|
||||||
} else if (type_entry->data.integral.is_signed) {
|
|
||||||
return LLVMBuildNSWSub(g->builder, op1_value, op2_value, "");
|
|
||||||
} else {
|
|
||||||
return LLVMBuildNUWSub(g->builder, op1_value, op2_value, "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
zig_unreachable();
|
|
||||||
}
|
|
||||||
case IrBinOpMult:
|
|
||||||
case IrBinOpMultWrap:
|
|
||||||
if (type_entry->id == ZigTypeIdFloat) {
|
|
||||||
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
|
|
||||||
return LLVMBuildFMul(g->builder, op1_value, op2_value, "");
|
|
||||||
} else if (type_entry->id == ZigTypeIdInt) {
|
|
||||||
bool is_wrapping = (op_id == IrBinOpMultWrap);
|
|
||||||
if (is_wrapping) {
|
|
||||||
return LLVMBuildMul(g->builder, op1_value, op2_value, "");
|
|
||||||
} else if (want_runtime_safety) {
|
|
||||||
return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value);
|
|
||||||
} else if (type_entry->data.integral.is_signed) {
|
|
||||||
return LLVMBuildNSWMul(g->builder, op1_value, op2_value, "");
|
|
||||||
} else {
|
|
||||||
return LLVMBuildNUWMul(g->builder, op1_value, op2_value, "");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
zig_unreachable();
|
|
||||||
}
|
|
||||||
case IrBinOpDivUnspecified:
|
case IrBinOpDivUnspecified:
|
||||||
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||||
op1_value, op2_value, type_entry, DivKindFloat);
|
op1_value, op2_value, type_entry, DivKindFloat);
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const mem = std.mem;
|
||||||
const assertOrPanic = std.debug.assertOrPanic;
|
const assertOrPanic = std.debug.assertOrPanic;
|
||||||
|
|
||||||
test "implicit array to vector and vector to array" {
|
test "vector wrap operators" {
|
||||||
const S = struct {
|
const S = struct {
|
||||||
fn doTheTest() void {
|
fn doTheTest() void {
|
||||||
var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
|
const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
|
||||||
const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
|
const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
|
||||||
v +%= x;
|
assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));
|
||||||
const result: [4]i32 = v;
|
assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));
|
||||||
assertOrPanic(result[0] == 11);
|
assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));
|
||||||
assertOrPanic(result[1] == 22);
|
|
||||||
assertOrPanic(result[2] == 33);
|
|
||||||
assertOrPanic(result[3] == 44);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
S.doTheTest();
|
S.doTheTest();
|
||||||
comptime S.doTheTest();
|
comptime S.doTheTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue