| ... | ... | @@ -455,55 +455,6 @@ static LLVMValueRef gen_widen_or_shorten(CodeGen *g, AstNode *source_node, TypeT |
| 455 | 455 | } |
| 456 | 456 | } |
| 457 | 457 | |
| 458 | | static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMValueRef array_ptr, |
| 459 | | TypeTableEntry *array_type, LLVMValueRef subscript_value) |
| 460 | | { |
| 461 | | assert(subscript_value); |
| 462 | | |
| 463 | | if (!type_has_bits(array_type)) { |
| 464 | | return nullptr; |
| 465 | | } |
| 466 | | |
| 467 | | if (array_type->id == TypeTableEntryIdArray) { |
| 468 | | if (want_debug_safety(g, source_node)) { |
| 469 | | LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref, |
| 470 | | array_type->data.array.len, false); |
| 471 | | add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); |
| 472 | | } |
| 473 | | LLVMValueRef indices[] = { |
| 474 | | LLVMConstNull(g->builtin_types.entry_usize->type_ref), |
| 475 | | subscript_value |
| 476 | | }; |
| 477 | | return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); |
| 478 | | } else if (array_type->id == TypeTableEntryIdPointer) { |
| 479 | | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 480 | | LLVMValueRef indices[] = { |
| 481 | | subscript_value |
| 482 | | }; |
| 483 | | return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); |
| 484 | | } else if (array_type->id == TypeTableEntryIdStruct) { |
| 485 | | assert(array_type->data.structure.is_slice); |
| 486 | | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 487 | | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); |
| 488 | | |
| 489 | | if (want_debug_safety(g, source_node)) { |
| 490 | | size_t len_index = array_type->data.structure.fields[1].gen_index; |
| 491 | | assert(len_index != SIZE_MAX); |
| 492 | | LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, ""); |
| 493 | | LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, ""); |
| 494 | | add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); |
| 495 | | } |
| 496 | | |
| 497 | | size_t ptr_index = array_type->data.structure.fields[0].gen_index; |
| 498 | | assert(ptr_index != SIZE_MAX); |
| 499 | | LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, ""); |
| 500 | | LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, ""); |
| 501 | | return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, ""); |
| 502 | | } else { |
| 503 | | zig_unreachable(); |
| 504 | | } |
| 505 | | } |
| 506 | | |
| 507 | 458 | static LLVMValueRef gen_overflow_op(CodeGen *g, TypeTableEntry *type_entry, AddSubMul op, |
| 508 | 459 | LLVMValueRef val1, LLVMValueRef val2) |
| 509 | 460 | { |
| ... | ... | @@ -640,83 +591,98 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns |
| 640 | 591 | return nullptr; |
| 641 | 592 | } |
| 642 | 593 | |
| 643 | | static LLVMValueRef ir_render_bin_op_bool(CodeGen *g, IrExecutable *executable, |
| 644 | | IrInstructionBinOp *bin_op_instruction) |
| 594 | static LLVMValueRef gen_overflow_shl_op(CodeGen *g, TypeTableEntry *type_entry, |
| 595 | LLVMValueRef val1, LLVMValueRef val2) |
| 645 | 596 | { |
| 646 | | IrBinOp op_id = bin_op_instruction->op_id; |
| 647 | | LLVMValueRef op1 = ir_llvm_value(g, bin_op_instruction->op1); |
| 648 | | LLVMValueRef op2 = ir_llvm_value(g, bin_op_instruction->op2); |
| 649 | | if (op_id == IrBinOpBoolOr) { |
| 650 | | return LLVMBuildOr(g->builder, op1, op2, ""); |
| 651 | | } else if (op_id == IrBinOpBoolAnd) { |
| 652 | | return LLVMBuildAnd(g->builder, op1, op2, ""); |
| 597 | // for unsigned left shifting, we do the wrapping shift, then logically shift |
| 598 | // right the same number of bits |
| 599 | // if the values don't match, we have an overflow |
| 600 | // for signed left shifting we do the same except arithmetic shift right |
| 601 | |
| 602 | assert(type_entry->id == TypeTableEntryIdInt); |
| 603 | |
| 604 | LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); |
| 605 | LLVMValueRef orig_val; |
| 606 | if (type_entry->data.integral.is_signed) { |
| 607 | orig_val = LLVMBuildAShr(g->builder, result, val2, ""); |
| 653 | 608 | } else { |
| 654 | | zig_unreachable(); |
| 609 | orig_val = LLVMBuildLShr(g->builder, result, val2, ""); |
| 655 | 610 | } |
| 611 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, ""); |
| 612 | |
| 613 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowOk"); |
| 614 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "OverflowFail"); |
| 615 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 616 | |
| 617 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| 618 | gen_debug_safety_crash(g); |
| 619 | |
| 620 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 621 | return result; |
| 656 | 622 | } |
| 657 | 623 | |
| 658 | | static LLVMValueRef ir_render_bin_op_cmp(CodeGen *g, IrExecutable *executable, |
| 659 | | IrInstructionBinOp *bin_op_instruction) |
| 624 | static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, |
| 625 | TypeTableEntry *type_entry, bool exact) |
| 660 | 626 | { |
| 661 | | IrBinOp op_id = bin_op_instruction->op_id; |
| 662 | | LLVMValueRef val1 = ir_llvm_value(g, bin_op_instruction->op1); |
| 663 | | LLVMValueRef val2 = ir_llvm_value(g, bin_op_instruction->op2); |
| 664 | | |
| 665 | | TypeTableEntry *op1_type = bin_op_instruction->op1->type_entry; |
| 666 | | TypeTableEntry *op2_type = bin_op_instruction->op2->type_entry; |
| 667 | | assert(op1_type == op2_type); |
| 668 | | |
| 669 | | if (op1_type->id == TypeTableEntryIdFloat) { |
| 670 | | LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); |
| 671 | | return LLVMBuildFCmp(g->builder, pred, val1, val2, ""); |
| 672 | | } else if (op1_type->id == TypeTableEntryIdInt) { |
| 673 | | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, op1_type->data.integral.is_signed); |
| 674 | | return LLVMBuildICmp(g->builder, pred, val1, val2, ""); |
| 675 | | } else if (op1_type->id == TypeTableEntryIdEnum) { |
| 676 | | if (op1_type->data.enumeration.gen_field_count == 0) { |
| 677 | | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); |
| 678 | | return LLVMBuildICmp(g->builder, pred, val1, val2, ""); |
| 627 | |
| 628 | if (want_debug_safety(g, source_node)) { |
| 629 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); |
| 630 | LLVMValueRef is_zero_bit; |
| 631 | if (type_entry->id == TypeTableEntryIdInt) { |
| 632 | is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); |
| 633 | } else if (type_entry->id == TypeTableEntryIdFloat) { |
| 634 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); |
| 679 | 635 | } else { |
| 680 | 636 | zig_unreachable(); |
| 681 | 637 | } |
| 682 | | } else if (op1_type->id == TypeTableEntryIdPureError || |
| 683 | | op1_type->id == TypeTableEntryIdPointer || |
| 684 | | op1_type->id == TypeTableEntryIdBool) |
| 685 | | { |
| 686 | | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); |
| 687 | | return LLVMBuildICmp(g->builder, pred, val1, val2, ""); |
| 688 | | } else { |
| 689 | | zig_unreachable(); |
| 638 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk"); |
| 639 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail"); |
| 640 | LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block); |
| 641 | |
| 642 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| 643 | gen_debug_safety_crash(g); |
| 644 | |
| 645 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 690 | 646 | } |
| 691 | | } |
| 692 | 647 | |
| 693 | | static LLVMValueRef ir_render_bin_op_add(CodeGen *g, IrExecutable *executable, |
| 694 | | IrInstructionBinOp *bin_op_instruction) |
| 695 | | { |
| 696 | | IrBinOp op_id = bin_op_instruction->op_id; |
| 697 | | IrInstruction *op1 = bin_op_instruction->op1; |
| 698 | | IrInstruction *op2 = bin_op_instruction->op2; |
| 648 | if (type_entry->id == TypeTableEntryIdFloat) { |
| 649 | assert(!exact); |
| 650 | return LLVMBuildFDiv(g->builder, val1, val2, ""); |
| 651 | } |
| 699 | 652 | |
| 700 | | assert(op1->type_entry == op2->type_entry); |
| 653 | assert(type_entry->id == TypeTableEntryIdInt); |
| 701 | 654 | |
| 702 | | LLVMValueRef op1_value = ir_llvm_value(g, op1); |
| 703 | | LLVMValueRef op2_value = ir_llvm_value(g, op2); |
| 655 | if (exact) { |
| 656 | if (want_debug_safety(g, source_node)) { |
| 657 | LLVMValueRef remainder_val; |
| 658 | if (type_entry->data.integral.is_signed) { |
| 659 | remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); |
| 660 | } else { |
| 661 | remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); |
| 662 | } |
| 663 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); |
| 664 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); |
| 704 | 665 | |
| 705 | | if (op1->type_entry->id == TypeTableEntryIdFloat) { |
| 706 | | return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); |
| 707 | | } else if (op1->type_entry->id == TypeTableEntryIdInt) { |
| 708 | | bool is_wrapping = (op_id == IrBinOpAddWrap); |
| 709 | | if (is_wrapping) { |
| 710 | | return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); |
| 711 | | } else if (ir_want_debug_safety(g, &bin_op_instruction->base)) { |
| 712 | | return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1_value, op2_value); |
| 713 | | } else if (op1->type_entry->data.integral.is_signed) { |
| 714 | | return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); |
| 666 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactOk"); |
| 667 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivExactFail"); |
| 668 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); |
| 669 | |
| 670 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| 671 | gen_debug_safety_crash(g); |
| 672 | |
| 673 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 674 | } |
| 675 | if (type_entry->data.integral.is_signed) { |
| 676 | return LLVMBuildExactSDiv(g->builder, val1, val2, ""); |
| 715 | 677 | } else { |
| 716 | | return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); |
| 678 | return ZigLLVMBuildExactUDiv(g->builder, val1, val2, ""); |
| 717 | 679 | } |
| 718 | 680 | } else { |
| 719 | | zig_unreachable(); |
| 681 | if (type_entry->data.integral.is_signed) { |
| 682 | return LLVMBuildSDiv(g->builder, val1, val2, ""); |
| 683 | } else { |
| 684 | return LLVMBuildUDiv(g->builder, val1, val2, ""); |
| 685 | } |
| 720 | 686 | } |
| 721 | 687 | } |
| 722 | 688 | |
| ... | ... | @@ -724,37 +690,146 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, |
| 724 | 690 | IrInstructionBinOp *bin_op_instruction) |
| 725 | 691 | { |
| 726 | 692 | IrBinOp op_id = bin_op_instruction->op_id; |
| 693 | IrInstruction *op1 = bin_op_instruction->op1; |
| 694 | IrInstruction *op2 = bin_op_instruction->op2; |
| 695 | AstNode *source_node = bin_op_instruction->base.source_node; |
| 696 | |
| 697 | assert(op1->type_entry == op2->type_entry); |
| 698 | |
| 699 | LLVMValueRef op1_value = ir_llvm_value(g, op1); |
| 700 | LLVMValueRef op2_value = ir_llvm_value(g, op2); |
| 727 | 701 | switch (op_id) { |
| 728 | 702 | case IrBinOpInvalid: |
| 729 | 703 | case IrBinOpArrayCat: |
| 730 | 704 | case IrBinOpArrayMult: |
| 731 | 705 | zig_unreachable(); |
| 732 | 706 | case IrBinOpBoolOr: |
| 707 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); |
| 733 | 708 | case IrBinOpBoolAnd: |
| 734 | | return ir_render_bin_op_bool(g, executable, bin_op_instruction); |
| 709 | return LLVMBuildAnd(g->builder, op1_value, op2_value, ""); |
| 735 | 710 | case IrBinOpCmpEq: |
| 736 | 711 | case IrBinOpCmpNotEq: |
| 737 | 712 | case IrBinOpCmpLessThan: |
| 738 | 713 | case IrBinOpCmpGreaterThan: |
| 739 | 714 | case IrBinOpCmpLessOrEq: |
| 740 | 715 | case IrBinOpCmpGreaterOrEq: |
| 741 | | return ir_render_bin_op_cmp(g, executable, bin_op_instruction); |
| 716 | if (op1->type_entry->id == TypeTableEntryIdFloat) { |
| 717 | LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); |
| 718 | return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); |
| 719 | } else if (op1->type_entry->id == TypeTableEntryIdInt) { |
| 720 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, op1->type_entry->data.integral.is_signed); |
| 721 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); |
| 722 | } else if (op1->type_entry->id == TypeTableEntryIdEnum) { |
| 723 | if (op1->type_entry->data.enumeration.gen_field_count == 0) { |
| 724 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); |
| 725 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); |
| 726 | } else { |
| 727 | zig_unreachable(); |
| 728 | } |
| 729 | } else if (op1->type_entry->id == TypeTableEntryIdPureError || |
| 730 | op1->type_entry->id == TypeTableEntryIdPointer || |
| 731 | op1->type_entry->id == TypeTableEntryIdBool) |
| 732 | { |
| 733 | LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); |
| 734 | return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); |
| 735 | } else { |
| 736 | zig_unreachable(); |
| 737 | } |
| 742 | 738 | case IrBinOpAdd: |
| 743 | 739 | case IrBinOpAddWrap: |
| 744 | | return ir_render_bin_op_add(g, executable, bin_op_instruction); |
| 740 | if (op1->type_entry->id == TypeTableEntryIdFloat) { |
| 741 | return LLVMBuildFAdd(g->builder, op1_value, op2_value, ""); |
| 742 | } else if (op1->type_entry->id == TypeTableEntryIdInt) { |
| 743 | bool is_wrapping = (op_id == IrBinOpAddWrap); |
| 744 | if (is_wrapping) { |
| 745 | return LLVMBuildAdd(g->builder, op1_value, op2_value, ""); |
| 746 | } else if (ir_want_debug_safety(g, &bin_op_instruction->base)) { |
| 747 | return gen_overflow_op(g, op1->type_entry, AddSubMulAdd, op1_value, op2_value); |
| 748 | } else if (op1->type_entry->data.integral.is_signed) { |
| 749 | return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, ""); |
| 750 | } else { |
| 751 | return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, ""); |
| 752 | } |
| 753 | } else { |
| 754 | zig_unreachable(); |
| 755 | } |
| 745 | 756 | case IrBinOpBinOr: |
| 757 | return LLVMBuildOr(g->builder, op1_value, op2_value, ""); |
| 746 | 758 | case IrBinOpBinXor: |
| 759 | return LLVMBuildXor(g->builder, op1_value, op2_value, ""); |
| 747 | 760 | case IrBinOpBinAnd: |
| 761 | return LLVMBuildAnd(g->builder, op1_value, op2_value, ""); |
| 748 | 762 | case IrBinOpBitShiftLeft: |
| 749 | 763 | case IrBinOpBitShiftLeftWrap: |
| 764 | { |
| 765 | assert(op1->type_entry->id == TypeTableEntryIdInt); |
| 766 | bool is_wrapping = (op_id == IrBinOpBitShiftLeftWrap); |
| 767 | if (is_wrapping) { |
| 768 | return LLVMBuildShl(g->builder, op1_value, op2_value, ""); |
| 769 | } else if (want_debug_safety(g, source_node)) { |
| 770 | return gen_overflow_shl_op(g, op1->type_entry, op1_value, op2_value); |
| 771 | } else if (op1->type_entry->data.integral.is_signed) { |
| 772 | return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_value, ""); |
| 773 | } else { |
| 774 | return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_value, ""); |
| 775 | } |
| 776 | } |
| 750 | 777 | case IrBinOpBitShiftRight: |
| 778 | assert(op1->type_entry->id == TypeTableEntryIdInt); |
| 779 | if (op1->type_entry->data.integral.is_signed) { |
| 780 | return LLVMBuildAShr(g->builder, op1_value, op2_value, ""); |
| 781 | } else { |
| 782 | return LLVMBuildLShr(g->builder, op1_value, op2_value, ""); |
| 783 | } |
| 751 | 784 | case IrBinOpSub: |
| 752 | 785 | case IrBinOpSubWrap: |
| 786 | if (op1->type_entry->id == TypeTableEntryIdFloat) { |
| 787 | return LLVMBuildFSub(g->builder, op1_value, op2_value, ""); |
| 788 | } else if (op1->type_entry->id == TypeTableEntryIdInt) { |
| 789 | bool is_wrapping = (op_id == IrBinOpSubWrap); |
| 790 | if (is_wrapping) { |
| 791 | return LLVMBuildSub(g->builder, op1_value, op2_value, ""); |
| 792 | } else if (want_debug_safety(g, source_node)) { |
| 793 | return gen_overflow_op(g, op1->type_entry, AddSubMulSub, op1_value, op2_value); |
| 794 | } else if (op1->type_entry->data.integral.is_signed) { |
| 795 | return LLVMBuildNSWSub(g->builder, op1_value, op2_value, ""); |
| 796 | } else { |
| 797 | return LLVMBuildNUWSub(g->builder, op1_value, op2_value, ""); |
| 798 | } |
| 799 | } else { |
| 800 | zig_unreachable(); |
| 801 | } |
| 753 | 802 | case IrBinOpMult: |
| 754 | 803 | case IrBinOpMultWrap: |
| 804 | if (op1->type_entry->id == TypeTableEntryIdFloat) { |
| 805 | return LLVMBuildFMul(g->builder, op1_value, op2_value, ""); |
| 806 | } else if (op1->type_entry->id == TypeTableEntryIdInt) { |
| 807 | bool is_wrapping = (op_id == IrBinOpMultWrap); |
| 808 | if (is_wrapping) { |
| 809 | return LLVMBuildMul(g->builder, op1_value, op2_value, ""); |
| 810 | } else if (want_debug_safety(g, source_node)) { |
| 811 | return gen_overflow_op(g, op1->type_entry, AddSubMulMul, op1_value, op2_value); |
| 812 | } else if (op1->type_entry->data.integral.is_signed) { |
| 813 | return LLVMBuildNSWMul(g->builder, op1_value, op2_value, ""); |
| 814 | } else { |
| 815 | return LLVMBuildNUWMul(g->builder, op1_value, op2_value, ""); |
| 816 | } |
| 817 | } else { |
| 818 | zig_unreachable(); |
| 819 | } |
| 755 | 820 | case IrBinOpDiv: |
| 821 | return gen_div(g, source_node, op1_value, op2_value, op1->type_entry, false); |
| 756 | 822 | case IrBinOpMod: |
| 757 | | zig_panic("TODO render more bin ops to LLVM"); |
| 823 | if (op1->type_entry->id == TypeTableEntryIdFloat) { |
| 824 | return LLVMBuildFRem(g->builder, op1_value, op2_value, ""); |
| 825 | } else { |
| 826 | assert(op1->type_entry->id == TypeTableEntryIdInt); |
| 827 | if (op1->type_entry->data.integral.is_signed) { |
| 828 | return LLVMBuildSRem(g->builder, op1_value, op2_value, ""); |
| 829 | } else { |
| 830 | return LLVMBuildURem(g->builder, op1_value, op2_value, ""); |
| 831 | } |
| 832 | } |
| 758 | 833 | } |
| 759 | 834 | zig_unreachable(); |
| 760 | 835 | } |
| ... | ... | @@ -1242,10 +1317,56 @@ static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutable *executable, IrIn |
| 1242 | 1317 | |
| 1243 | 1318 | static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrInstructionElemPtr *instruction) { |
| 1244 | 1319 | LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->array_ptr); |
| 1245 | | LLVMValueRef array_ptr = LLVMBuildLoad(g->builder, array_ptr_ptr, ""); |
| 1320 | TypeTableEntry *array_ptr_type = instruction->array_ptr->type_entry; |
| 1321 | assert(array_ptr_type->id == TypeTableEntryIdPointer); |
| 1322 | TypeTableEntry *array_type = array_ptr_type->data.pointer.child_type; |
| 1323 | LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type); |
| 1246 | 1324 | LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index); |
| 1247 | | TypeTableEntry *array_type = instruction->array_ptr->type_entry; |
| 1248 | | return gen_array_elem_ptr(g, instruction->base.source_node, array_ptr, array_type, subscript_value); |
| 1325 | assert(subscript_value); |
| 1326 | |
| 1327 | if (!type_has_bits(array_type)) |
| 1328 | return nullptr; |
| 1329 | |
| 1330 | bool safety_check_on = ir_want_debug_safety(g, &instruction->base) && instruction->safety_check_on; |
| 1331 | |
| 1332 | if (array_type->id == TypeTableEntryIdArray) { |
| 1333 | if (safety_check_on) { |
| 1334 | LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->type_ref, |
| 1335 | array_type->data.array.len, false); |
| 1336 | add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); |
| 1337 | } |
| 1338 | LLVMValueRef indices[] = { |
| 1339 | LLVMConstNull(g->builtin_types.entry_usize->type_ref), |
| 1340 | subscript_value |
| 1341 | }; |
| 1342 | return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); |
| 1343 | } else if (array_type->id == TypeTableEntryIdPointer) { |
| 1344 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 1345 | LLVMValueRef indices[] = { |
| 1346 | subscript_value |
| 1347 | }; |
| 1348 | return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); |
| 1349 | } else if (array_type->id == TypeTableEntryIdStruct) { |
| 1350 | assert(array_type->data.structure.is_slice); |
| 1351 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 1352 | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); |
| 1353 | |
| 1354 | if (safety_check_on) { |
| 1355 | size_t len_index = array_type->data.structure.fields[1].gen_index; |
| 1356 | assert(len_index != SIZE_MAX); |
| 1357 | LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, ""); |
| 1358 | LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, ""); |
| 1359 | add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); |
| 1360 | } |
| 1361 | |
| 1362 | size_t ptr_index = array_type->data.structure.fields[0].gen_index; |
| 1363 | assert(ptr_index != SIZE_MAX); |
| 1364 | LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, ""); |
| 1365 | LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, ""); |
| 1366 | return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, ""); |
| 1367 | } else { |
| 1368 | zig_unreachable(); |
| 1369 | } |
| 1249 | 1370 | } |
| 1250 | 1371 | |
| 1251 | 1372 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { |
| ... | ... | @@ -1423,7 +1544,6 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru |
| 1423 | 1544 | LLVMValueRef asm_fn = LLVMConstInlineAsm(function_type, buf_ptr(&llvm_template), |
| 1424 | 1545 | buf_ptr(&constraint_buf), is_volatile, false); |
| 1425 | 1546 | |
| 1426 | | set_debug_source_node(g, asm_node); |
| 1427 | 1547 | return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); |
| 1428 | 1548 | } |
| 1429 | 1549 | |