Commit 3429639e84

Andrew Kelley <superjoe30@gmail.com>
2016-12-11 21:31:07
IR: implement truncate builtin
1 parent 433c17a
src/all_types.hpp
@@ -1410,6 +1410,7 @@ enum IrInstructionId {
     IrInstructionIdCmpxchg,
     IrInstructionIdFence,
     IrInstructionIdDivExact,
+    IrInstructionIdTruncate,
 };
 
 struct IrInstruction {
@@ -1892,6 +1893,13 @@ struct IrInstructionDivExact {
     IrInstruction *op2;
 };
 
+struct IrInstructionTruncate {
+    IrInstruction base;
+
+    IrInstruction *dest_type;
+    IrInstruction *target;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/codegen.cpp
@@ -1879,6 +1879,14 @@ static LLVMValueRef ir_render_div_exact(CodeGen *g, IrExecutable *executable, Ir
     return gen_div(g, want_debug_safety, op1_val, op2_val, instruction->base.type_entry, true);
 }
 
+static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrInstructionTruncate *instruction) {
+    assert(instruction->dest_type->type_entry->id == TypeTableEntryIdMetaType);
+    TypeTableEntry *dest_type = get_underlying_type(instruction->dest_type->static_value.data.x_type);
+    assert(dest_type->id == TypeTableEntryIdInt);
+    LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
+    return LLVMBuildTrunc(g->builder, target_val, dest_type->type_ref, "");
+}
+
 static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
     AstNode *source_node = instruction->source_node;
     Scope *scope = instruction->scope;
@@ -1974,6 +1982,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
             return ir_render_fence(g, executable, (IrInstructionFence *)instruction);
         case IrInstructionIdDivExact:
             return ir_render_div_exact(g, executable, (IrInstructionDivExact *)instruction);
+        case IrInstructionIdTruncate:
+            return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
         case IrInstructionIdSwitchVar:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdStructInit:
src/ir.cpp
@@ -359,6 +359,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionDivExact *) {
     return IrInstructionIdDivExact;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) {
+    return IrInstructionIdTruncate;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -1434,6 +1438,23 @@ static IrInstruction *ir_build_div_exact_from(IrBuilder *irb, IrInstruction *old
     return new_instruction;
 }
 
+static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
+    IrInstructionTruncate *instruction = ir_build_instruction<IrInstructionTruncate>(irb, scope, source_node);
+    instruction->dest_type = dest_type;
+    instruction->target = target;
+
+    ir_ref_instruction(dest_type);
+    ir_ref_instruction(target);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_truncate_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *dest_type, IrInstruction *target) {
+    IrInstruction *new_instruction = ir_build_truncate(irb, old_instruction->scope, old_instruction->source_node, dest_type, target);
+    ir_link_new_instruction(new_instruction, old_instruction);
+    return new_instruction;
+}
+
 static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope,
         bool gen_error_defers, bool gen_maybe_defers)
 {
@@ -2227,6 +2248,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
 
                 return ir_build_div_exact(irb, scope, node, arg0_value, arg1_value);
             }
+        case BuiltinFnIdTruncate:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+                if (arg1_value == irb->codegen->invalid_instruction)
+                    return arg1_value;
+
+                return ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
+            }
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
         case BuiltinFnIdAlignof:
@@ -2238,7 +2273,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
         case BuiltinFnIdBreakpoint:
         case BuiltinFnIdReturnAddress:
         case BuiltinFnIdFrameAddress:
-        case BuiltinFnIdTruncate:
         case BuiltinFnIdIntType:
             zig_panic("TODO IR gen more builtin functions");
     }
@@ -7102,26 +7136,27 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
 {
     TypeTableEntry *target_type = ir_resolve_type(ira, target_type_value);
     bool depends_on_compile_var = target_type_value->static_value.depends_on_compile_var;
-    switch (target_type->id) {
+    TypeTableEntry *canon_type = get_underlying_type(target_type);
+    switch (canon_type->id) {
         case TypeTableEntryIdInvalid:
             return ira->codegen->builtin_types.entry_invalid;
         case TypeTableEntryIdInt:
             {
                 ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
-                eval_min_max_value(ira->codegen, target_type, out_val, is_max);
+                eval_min_max_value(ira->codegen, canon_type, out_val, is_max);
                 return ira->codegen->builtin_types.entry_num_lit_int;
             }
         case TypeTableEntryIdFloat:
             {
                 ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
-                eval_min_max_value(ira->codegen, target_type, out_val, is_max);
+                eval_min_max_value(ira->codegen, canon_type, out_val, is_max);
                 return ira->codegen->builtin_types.entry_num_lit_float;
             }
         case TypeTableEntryIdBool:
         case TypeTableEntryIdVoid:
             {
                 ConstExprValue *out_val = ir_build_const_from(ira, source_instruction, depends_on_compile_var);
-                eval_min_max_value(ira->codegen, target_type, out_val, is_max);
+                eval_min_max_value(ira->codegen, canon_type, out_val, is_max);
                 return target_type;
             }
         case TypeTableEntryIdVar:
@@ -7150,6 +7185,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
                     "no min value available for type '%s'";
                 ir_add_error(ira, source_instruction,
                         buf_sprintf(err_format, buf_ptr(&target_type->name)));
+                // TODO if this is a typedecl, add error note showing the declaration of the type decl
                 return ira->codegen->builtin_types.entry_invalid;
             }
     }
@@ -7530,6 +7566,60 @@ static TypeTableEntry *ir_analyze_instruction_div_exact(IrAnalyze *ira, IrInstru
     return result_type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) {
+    IrInstruction *dest_type_value = instruction->dest_type->other;
+    TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
+    TypeTableEntry *canon_dest_type = get_underlying_type(dest_type);
+
+    if (canon_dest_type->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (canon_dest_type->id != TypeTableEntryIdInt &&
+        canon_dest_type->id != TypeTableEntryIdNumLitInt)
+    {
+        ir_add_error(ira, dest_type_value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
+        // TODO if meta_type is type decl, add note pointing to type decl declaration
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    IrInstruction *target = instruction->target->other;
+    TypeTableEntry *src_type = target->type_entry;
+    TypeTableEntry *canon_src_type = get_underlying_type(src_type);
+    if (canon_src_type->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    if (canon_src_type->id != TypeTableEntryIdInt &&
+        canon_src_type->id != TypeTableEntryIdNumLitInt)
+    {
+        ir_add_error(ira, target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name)));
+        // TODO if meta_type is type decl, add note pointing to type decl declaration
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    if (canon_src_type->data.integral.is_signed != canon_dest_type->data.integral.is_signed) {
+        const char *sign_str = canon_dest_type->data.integral.is_signed ? "signed" : "unsigned";
+        ir_add_error(ira, target, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name)));
+        // TODO if meta_type is type decl, add note pointing to type decl declaration
+        return ira->codegen->builtin_types.entry_invalid;
+    } else if (canon_src_type->data.integral.bit_count <= canon_dest_type->data.integral.bit_count) {
+        ir_add_error(ira, target, buf_sprintf("type '%s' has same or fewer bits than destination type '%s'",
+                    buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
+        // TODO if meta_type is type decl, add note pointing to type decl declaration
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    if (target->static_value.special == ConstValSpecialStatic) {
+        bool depends_on_compile_var = dest_type_value->static_value.depends_on_compile_var || target->static_value.depends_on_compile_var;
+        ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
+        bignum_init_bignum(&out_val->data.x_bignum, &target->static_value.data.x_bignum);
+        bignum_truncate(&out_val->data.x_bignum, canon_dest_type->data.integral.bit_count);
+        return dest_type;
+    }
+
+    ir_build_truncate_from(&ira->new_irb, &instruction->base, dest_type_value, target);
+    return dest_type;
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -7638,6 +7728,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);
         case IrInstructionIdDivExact:
             return ir_analyze_instruction_div_exact(ira, (IrInstructionDivExact *)instruction);
+        case IrInstructionIdTruncate:
+            return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction);
         case IrInstructionIdCast:
         case IrInstructionIdStructFieldPtr:
         case IrInstructionIdEnumFieldPtr:
@@ -7776,6 +7868,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdErrName:
         case IrInstructionIdEmbedFile:
         case IrInstructionIdDivExact:
+        case IrInstructionIdTruncate:
             return false;
         case IrInstructionIdAsm:
             {
@@ -7787,46 +7880,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 }
 
 // TODO port over all this commented out code into new IR way of doing things
-
-
-//static TypeTableEntry *analyze_truncate(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *context, AstNode *node)
-//{
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    AstNode **op1 = &node->data.fn_call_expr.params.at(0);
-//    AstNode **op2 = &node->data.fn_call_expr.params.at(1);
-//
-//    TypeTableEntry *dest_type = analyze_type_expr(g, import, context, *op1);
-//    TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, *op2);
-//
-//    if (dest_type->id == TypeTableEntryIdInvalid || src_type->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    } else if (dest_type->id != TypeTableEntryIdInt) {
-//        add_node_error(g, *op1,
-//                buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
-//        return g->builtin_types.entry_invalid;
-//    } else if (src_type->id != TypeTableEntryIdInt) {
-//        add_node_error(g, *op2,
-//                buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name)));
-//        return g->builtin_types.entry_invalid;
-//    } else if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
-//        const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned";
-//        add_node_error(g, *op2,
-//                buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name)));
-//        return g->builtin_types.entry_invalid;
-//    } else if (src_type->data.integral.bit_count <= dest_type->data.integral.bit_count) {
-//        add_node_error(g, *op2,
-//                buf_sprintf("type '%s' has same or fewer bits than destination type '%s'",
-//                    buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    // TODO const expr eval
-//
-//    return dest_type;
-//}
-//
 //static TypeTableEntry *analyze_int_type(CodeGen *g, ImportTableEntry *import,
 //        BlockContext *context, AstNode *node)
 //{
@@ -7996,8 +8049,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //        case BuiltinFnIdFrameAddress:
 //            mark_impure_fn(g, context, node);
 //            return builtin_fn->return_type;
-//        case BuiltinFnIdTruncate:
-//            return analyze_truncate(g, import, context, node);
 //        case BuiltinFnIdIntType:
 //            return analyze_int_type(g, import, context, node);
 //    }
@@ -8315,19 +8366,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //    }
 //}
 //
-
-
-//static LLVMValueRef gen_truncate(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeFnCallExpr);
-//
-//    TypeTableEntry *dest_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0));
-//    AstNode *src_node = node->data.fn_call_expr.params.at(1);
-//
-//    LLVMValueRef src_val = gen_expr(g, src_node);
-//
-//    return LLVMBuildTrunc(g->builder, src_val, dest_type->type_ref, "");
-//}
-//
 //static LLVMValueRef gen_shl_with_overflow(CodeGen *g, AstNode *node) {
 //    assert(node->type == NodeTypeFnCallExpr);
 //
@@ -8483,8 +8521,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //            return gen_cmp_exchange(g, node);
 //        case BuiltinFnIdFence:
 //            return gen_fence(g, node);
-//        case BuiltinFnIdTruncate:
-//            return gen_truncate(g, node);
 //        case BuiltinFnIdUnreachable:
 //            zig_panic("moved to ir render");
 //        case BuiltinFnIdSetFnTest:
src/ir_print.cpp
@@ -747,6 +747,14 @@ static void ir_print_div_exact(IrPrint *irp, IrInstructionDivExact *instruction)
     fprintf(irp->f, ")");
 }
 
+static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction) {
+    fprintf(irp->f, "@truncate(");
+    ir_print_other_instruction(irp, instruction->dest_type);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->target);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -920,6 +928,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdDivExact:
             ir_print_div_exact(irp, (IrInstructionDivExact *)instruction);
             break;
+        case IrInstructionIdTruncate:
+            ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
test/self_hosted2.zig
@@ -292,6 +292,14 @@ fn divExact(a: u32, b: u32) -> u32 {
     @divExact(a, b)
 }
 
+fn truncate() {
+    assert(testTruncate(0x10fd) == 0xfd);
+}
+fn testTruncate(x: u32) -> u8 {
+    @truncate(u8, x)
+}
+
+
 fn assert(ok: bool) {
     if (!ok)
         @unreachable();
@@ -322,6 +330,7 @@ fn runAllTests() {
     cmpxchg();
     fence();
     exactDivision();
+    truncate();
 }
 
 export nakedcc fn _start() -> unreachable {