From 4010f6a11dafa1d047d66a637a0efe58d80a52c6 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 5 Feb 2019 11:00:32 +0100 Subject: [PATCH] Added support for vector wrapping mult and sub * I also merged the code that generates ir for add, sub, and mult --- src/codegen.cpp | 122 +++++++++++++------------------- test/stage1/behavior/vector.zig | 17 ++--- 2 files changed, 58 insertions(+), 81 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index de2222afb..226a13779 100644 --- a/src/codegen.cpp +++ b/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, IrInstructionBinOp *bin_op_instruction) { @@ -2640,50 +2642,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } else { zig_unreachable(); } + case IrBinOpMult: + case IrBinOpMultWrap: case IrBinOpAdd: 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) { 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 - return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, ""); + 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 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) { - bool is_wrapping = (op_id == IrBinOpAddWrap); 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) { - 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) { - return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); + return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); } else { - return LLVMBuildNUWAdd(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(); + return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); } } else { zig_unreachable(); } + } case IrBinOpBinOr: return LLVMBuildOr(g->builder, op1_value, op2_value, ""); case IrBinOpBinXor: @@ -2728,49 +2751,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, 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: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), op1_value, op2_value, type_entry, DivKindFloat); diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index 53c5d0138..c97ee0e3d 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -1,20 +1,17 @@ const std = @import("std"); +const mem = std.mem; const assertOrPanic = std.debug.assertOrPanic; -test "implicit array to vector and vector to array" { +test "vector wrap operators" { const S = struct { fn doTheTest() void { - var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40}; - const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4}; - v +%= x; - const result: [4]i32 = v; - assertOrPanic(result[0] == 11); - assertOrPanic(result[1] == 22); - assertOrPanic(result[2] == 33); - assertOrPanic(result[3] == 44); + const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 }; + const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 }; + assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 })); + assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 })); + assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 })); } }; S.doTheTest(); comptime S.doTheTest(); } -