From 855edd2949c6b3b36be4c3ba8d30f174ff8b8db1 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 22 Mar 2020 20:20:36 +0100 Subject: ir: Rewrite the bound checks in slice operator Closes #4777 --- src/codegen.cpp | 235 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 138 insertions(+), 97 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index c9e67dcac5..7a2ee6b2a3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5408,6 +5408,8 @@ static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, Ir } static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { + Error err; + LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); ZigType *array_ptr_type = instruction->ptr->value->type; assert(array_ptr_type->id == ZigTypeIdPointer); @@ -5416,15 +5418,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); + // The result is either a slice or a pointer to an array ZigType *result_type = instruction->base.value->type; - if (!type_has_bits(g, result_type)) { - return nullptr; - } // This is not whether the result type has a sentinel, but whether there should be a sentinel check, // e.g. if they used [a..b :s] syntax. ZigValue *sentinel = instruction->sentinel; + LLVMValueRef slice_start_ptr = nullptr; + LLVMValueRef len_value = nullptr; + if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) { @@ -5438,111 +5441,86 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } else { end_val = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false); } + if (want_runtime_safety) { + // Safety check: start <= end if (instruction->start->value->special == ConstValSpecialRuntime || instruction->end) { add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); } - if (instruction->end) { - LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, - array_type->data.array.len, false); - add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); - if (sentinel != nullptr) { - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - end_val, - }; - LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - add_sentinel_check(g, sentinel_elem_ptr, sentinel); - } - } - } - if (!type_has_bits(g, array_type)) { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + // Safety check: the last element of the slice (the sentinel if + // requested) must be inside the array + // XXX: Overflow is not checked here... + const size_t full_len = array_type->data.array.len + + (array_type->data.array.sentinel != nullptr); + LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, + full_len, false); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); - - // TODO if runtime safety is on, store 0xaaaaaaa in ptr field - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return tmp_struct_ptr; + LLVMValueRef check_end_val = end_val; + if (sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); + } + add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); } - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - start_val, - }; - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - if (result_type->id == ZigTypeIdPointer) { - ir_assert(instruction->result_loc == nullptr, &instruction->base); - LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - } else { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + bool value_has_bits; + if ((err = type_has_bits2(g, array_type, &value_has_bits))) + codegen_report_errors_and_exit(g); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + if (value_has_bits) { + if (want_runtime_safety && sentinel != nullptr) { + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + end_val, + }; + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } - return tmp_struct_ptr; + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + start_val, + }; + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); } + + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); } else if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.ptr_len != PtrLenSingle); LLVMValueRef start_val = ir_llvm_value(g, instruction->start); LLVMValueRef end_val = ir_llvm_value(g, instruction->end); if (want_runtime_safety) { + // Safety check: start <= end add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); - if (sentinel != nullptr) { + } + + bool value_has_bits; + if ((err = type_has_bits2(g, array_type, &value_has_bits))) + codegen_report_errors_and_exit(g); + + if (value_has_bits) { + if (want_runtime_safety && sentinel != nullptr) { LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); add_sentinel_check(g, sentinel_elem_ptr, sentinel); } - } - if (!type_has_bits(g, array_type)) { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return tmp_struct_ptr; + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); } - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); - if (result_type->id == ZigTypeIdPointer) { - ir_assert(instruction->result_loc == nullptr, &instruction->base); - LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - } - - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - - size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - - size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - - return tmp_struct_ptr; - + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); } else if (array_type->id == ZigTypeIdStruct) { assert(array_type->data.structure.special == StructSpecialSlice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; - assert(ptr_index != SIZE_MAX); - size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index; - assert(len_index != SIZE_MAX); + const size_t gen_len_index = array_type->data.structure.fields[slice_len_index]->gen_index; + assert(gen_len_index != SIZE_MAX); LLVMValueRef prev_end = nullptr; if (!instruction->end || want_runtime_safety) { - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); + LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_len_index, ""); prev_end = gen_load_untyped(g, src_len_ptr, 0, false, ""); } @@ -5554,41 +5532,104 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI end_val = prev_end; } - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); + ZigType *ptr_field_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; if (want_runtime_safety) { assert(prev_end); + // Safety check: start <= end add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); - if (instruction->end) { - add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); - if (sentinel != nullptr) { - LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); - add_sentinel_check(g, sentinel_elem_ptr, sentinel); - } + // Safety check: the sentinel counts as one more element + // XXX: Overflow is not checked here... + LLVMValueRef check_prev_end = prev_end; + if (ptr_field_type->data.pointer.sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_prev_end = LLVMBuildNUWAdd(g->builder, prev_end, usize_one, ""); + } + LLVMValueRef check_end_val = end_val; + if (sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); } + + add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, check_prev_end); } - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); - if (result_type->id == ZigTypeIdPointer) { - ir_assert(instruction->result_loc == nullptr, &instruction->base); - LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - } else { - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + bool ptr_has_bits; + if ((err = type_has_bits2(g, ptr_field_type, &ptr_has_bits))) + codegen_report_errors_and_exit(g); + + if (ptr_has_bits) { + const size_t gen_ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; + assert(gen_ptr_index != SIZE_MAX); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_ptr_index, ""); + LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); - return tmp_struct_ptr; + if (sentinel != nullptr) { + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } + + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); } + + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); } else { zig_unreachable(); } + + bool result_has_bits; + if ((err = type_has_bits2(g, result_type, &result_has_bits))) + codegen_report_errors_and_exit(g); + + // Nothing to do, we're only interested in the bound checks emitted above + if (!result_has_bits) + return nullptr; + + // The starting pointer for the slice may be null in case of zero-sized + // arrays, the length value is always defined. + assert(len_value != nullptr); + + // The slice decays into a pointer to an array, the size is tracked in the + // type itself + if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + + if (slice_start_ptr != nullptr) { + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + } + + return LLVMGetUndef(result_ptr_type); + } + + ir_assert(instruction->result_loc != nullptr, &instruction->base); + // Create a new slice + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + + ZigType *slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry; + + // The slice may not have a pointer at all if it points to a zero-sized type + const size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; + if (gen_ptr_index != SIZE_MAX) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); + if (slice_start_ptr != nullptr) { + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + } else if (want_runtime_safety) { + gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr); + } else { + gen_store_untyped(g, LLVMGetUndef(get_llvm_type(g, slice_ptr_type)), ptr_field_ptr, 0, false); + } + } + + const size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; + assert(gen_len_index != SIZE_MAX); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return tmp_struct_ptr; } static LLVMValueRef get_trap_fn_val(CodeGen *g) { -- cgit v1.2.3 From 11b50e3ad8781fd600961e80aad622c6f5616acc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 3 Apr 2020 11:02:22 -0400 Subject: change the default ABI of riscv64-linux-musl Before, this would cause a link failure when mixing Zig and C code for RISC-V targets. Now, the ABIs match and Zig and C code can be mixed successfully. I will file a follow-up issue for the ability to deal more explicitly with ABIs. closes #4863 --- src/codegen.cpp | 16 +++++++++++++++- src/zig_llvm.cpp | 17 ++++++++++++++++- src/zig_llvm.h | 9 ++++++++- test/tests.zig | 17 ++++++++--------- 4 files changed, 47 insertions(+), 12 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index c9e67dcac5..ed8fcf54d7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8940,10 +8940,24 @@ static void init(CodeGen *g) { fprintf(stderr, "name=%s target_specific_cpu_args=%s\n", buf_ptr(g->root_out_name), target_specific_cpu_args); fprintf(stderr, "name=%s target_specific_features=%s\n", buf_ptr(g->root_out_name), target_specific_features); } + + // TODO handle float ABI better- it should depend on the ABI portion of std.Target + ZigLLVMABIType float_abi = ZigLLVMABITypeDefault; + + // TODO a way to override this as part of std.Target ABI? + const char *abi_name = nullptr; + if (target_is_riscv(g->zig_target)) { + // RISC-V Linux defaults to ilp32d/lp64d + if (g->zig_target->os == OsLinux) { + abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32d" : "lp64d"; + } else { + abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64"; + } + } g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str), target_specific_cpu_args, target_specific_features, opt_level, reloc_mode, - to_llvm_code_model(g), g->function_sections); + to_llvm_code_model(g), g->function_sections, float_abi, abi_name); g->target_data_ref = LLVMCreateTargetDataLayout(g->target_machine); diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 5569f90cdc..e5b9df625c 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -100,7 +100,7 @@ static const bool assertions_on = false; LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, - LLVMCodeModel CodeModel, bool function_sections) + LLVMCodeModel CodeModel, bool function_sections, ZigLLVMABIType float_abi, const char *abi_name) { Optional RM; switch (Reloc){ @@ -147,6 +147,21 @@ LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Tri TargetOptions opt; opt.FunctionSections = function_sections; + switch (float_abi) { + case ZigLLVMABITypeDefault: + opt.FloatABIType = FloatABI::Default; + break; + case ZigLLVMABITypeSoft: + opt.FloatABIType = FloatABI::Soft; + break; + case ZigLLVMABITypeHard: + opt.FloatABIType = FloatABI::Hard; + break; + } + + if (abi_name != nullptr) { + opt.MCOptions.ABIName = abi_name; + } TargetMachine *TM = reinterpret_cast(T)->createTargetMachine(Triple, CPU, Features, opt, RM, CM, OL, JIT); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index c2c4b4dd77..f07684f2a4 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -51,9 +51,16 @@ ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machi bool is_small, bool time_report, const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename); + +enum ZigLLVMABIType { + ZigLLVMABITypeDefault, // Target-specific (either soft or hard depending on triple, etc). + ZigLLVMABITypeSoft, // Soft float. + ZigLLVMABITypeHard // Hard float. +}; + ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, - LLVMCodeModel CodeModel, bool function_sections); + LLVMCodeModel CodeModel, bool function_sections, ZigLLVMABIType float_abi, const char *abi_name); ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); diff --git a/test/tests.zig b/test/tests.zig index 9ada899b1b..6861270792 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -160,15 +160,14 @@ const test_targets = blk: { }, }, - // https://github.com/ziglang/zig/issues/4863 - //TestTarget{ - // .target = .{ - // .cpu_arch = .riscv64, - // .os_tag = .linux, - // .abi = .musl, - // }, - // .link_libc = true, - //}, + TestTarget{ + .target = .{ + .cpu_arch = .riscv64, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, // https://github.com/ziglang/zig/issues/3340 //TestTarget{ -- cgit v1.2.3 From c4a5f519f22511ce90bfe0aca0690b24cc592f88 Mon Sep 17 00:00:00 2001 From: Michaƫl Larouche Date: Sat, 4 Apr 2020 18:41:16 -0400 Subject: Do not parse native_libc.txt anymore when linking on native target, always run detection of libc. Fixes #4772 --- src/codegen.cpp | 81 ++++----------------------------------------------------- 1 file changed, 5 insertions(+), 76 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index ed8fcf54d7..84168f509f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9057,80 +9057,13 @@ static void detect_libc(CodeGen *g) { if (g->zig_target->is_native_os) { g->libc = heap::c_allocator.create(); - // search for native_libc.txt in following dirs: - // - LOCAL_CACHE_DIR - // - GLOBAL_CACHE_DIR - // if not found create at: - // - GLOBAL_CACHE_DIR - // be mindful local/global caches may be the same dir - - Buf basename = BUF_INIT; - buf_init_from_str(&basename, "native_libc.txt"); - - Buf local_libc_txt = BUF_INIT; - os_path_join(g->cache_dir, &basename, &local_libc_txt); - - Buf global_libc_txt = BUF_INIT; - os_path_join(get_global_cache_dir(), &basename, &global_libc_txt); - - Buf *pathnames[3] = { nullptr }; - size_t pathnames_idx = 0; - - pathnames[pathnames_idx] = &local_libc_txt; - pathnames_idx += 1; - - if (!buf_eql_buf(pathnames[0], &global_libc_txt)) { - pathnames[pathnames_idx] = &global_libc_txt; - pathnames_idx += 1; - } - - Buf* libc_txt = nullptr; - for (auto name : pathnames) { - if (name == nullptr) - break; - - bool result; - if (os_file_exists(name, &result) != ErrorNone || !result) - continue; - - libc_txt = name; - break; + if ((err = stage2_libc_find_native(g->libc))) { + fprintf(stderr, + "Unable to link against libc: Unable to find libc installation: %s\n" + "See `zig libc --help` for more details.\n", err_str(err)); + exit(1); } - if (libc_txt == nullptr) - libc_txt = &global_libc_txt; - - if ((err = stage2_libc_parse(g->libc, buf_ptr(libc_txt)))) { - if ((err = stage2_libc_find_native(g->libc))) { - fprintf(stderr, - "Unable to link against libc: Unable to find libc installation: %s\n" - "See `zig libc --help` for more details.\n", err_str(err)); - exit(1); - } - Buf libc_txt_dir = BUF_INIT; - os_path_dirname(libc_txt, &libc_txt_dir); - buf_deinit(&libc_txt_dir); - if ((err = os_make_path(&libc_txt_dir))) { - fprintf(stderr, "Unable to create %s directory: %s\n", - buf_ptr(g->cache_dir), err_str(err)); - exit(1); - } - Buf *native_libc_tmp = buf_sprintf("%s.tmp", buf_ptr(libc_txt)); - FILE *file = fopen(buf_ptr(native_libc_tmp), "wb"); - if (file == nullptr) { - fprintf(stderr, "Unable to open %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); - exit(1); - } - stage2_libc_render(g->libc, file); - if (fclose(file) != 0) { - fprintf(stderr, "Unable to save %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); - exit(1); - } - if ((err = os_rename(native_libc_tmp, libc_txt))) { - fprintf(stderr, "Unable to create %s: %s\n", buf_ptr(libc_txt), err_str(err)); - exit(1); - } - } bool want_sys_dir = !mem_eql_mem(g->libc->include_dir, g->libc->include_dir_len, g->libc->sys_include_dir, g->libc->sys_include_dir_len); size_t want_um_and_shared_dirs = (g->zig_target->os == OsWindows) ? 2 : 0; @@ -9164,10 +9097,6 @@ static void detect_libc(CodeGen *g) { g->libc_include_dir_len += 1; } assert(g->libc_include_dir_len == dir_count); - - buf_deinit(&global_libc_txt); - buf_deinit(&local_libc_txt); - buf_deinit(&basename); } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) && !target_os_is_darwin(g->zig_target->os)) { -- cgit v1.2.3 From 54ffcf95a8596aa3adf57cddbe079a24f849f408 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 14 Mar 2020 13:23:41 +0100 Subject: ir: Support div/mod/rem on vector types Closes #4050 --- src/codegen.cpp | 140 +++++++++++++------- src/ir.cpp | 284 ++++++++++++++++++++++------------------ test/stage1/behavior/vector.zig | 75 +++++++++++ 3 files changed, 326 insertions(+), 173 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 84168f509f..0fa181b32c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2591,12 +2591,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, } static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { - if ((op == BuiltinFnIdCeil || - op == BuiltinFnIdFloor) && - type_entry->id == ZigTypeIdInt) - return val; - assert(type_entry->id == ZigTypeIdFloat); - + assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); } @@ -2612,6 +2607,21 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { if (bigint->digit_count == 0) { return LLVMConstNull(type_ref); } + + if (LLVMGetTypeKind(type_ref) == LLVMVectorTypeKind) { + const unsigned vector_len = LLVMGetVectorSize(type_ref); + LLVMTypeRef elem_type = LLVMGetElementType(type_ref); + + LLVMValueRef *values = heap::c_allocator.allocate_nonzero(vector_len); + // Create a vector with all the elements having the same value + for (unsigned i = 0; i < vector_len; i++) { + values[i] = bigint_to_llvm_const(elem_type, bigint); + } + LLVMValueRef result = LLVMConstVector(values, vector_len); + heap::c_allocator.deallocate(values, vector_len); + return result; + } + LLVMValueRef unsigned_val; if (bigint->digit_count == 1) { unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); @@ -2625,22 +2635,40 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { } } +// Collapses a vector into a single i1 whose value is 1 iff all the +// vector elements are 1 +static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val) { + assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); + LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); + LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); + LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); + return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); +} + static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math, - LLVMValueRef val1, LLVMValueRef val2, - ZigType *type_entry, DivKind div_kind) + LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind) { + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + ZigLLVMSetFastMath(g->builder, want_fast_math); - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry)); - if (want_runtime_safety && (want_fast_math || type_entry->id != ZigTypeIdFloat)) { + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); + if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) { + // Safety check: divisor != 0 LLVMValueRef is_zero_bit; - if (type_entry->id == ZigTypeIdInt) { + if (scalar_type->id == ZigTypeIdInt) { is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); - } else if (type_entry->id == ZigTypeIdFloat) { + } else if (scalar_type->id == ZigTypeIdFloat) { is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); } else { zig_unreachable(); } + + if (operand_type->id == ZigTypeIdVector) { + is_zero_bit = scalarize_cmp_result(g, is_zero_bit); + } + LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); @@ -2650,16 +2678,21 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block); - if (type_entry->id == ZigTypeIdInt && type_entry->data.integral.is_signed) { - LLVMValueRef neg_1_value = LLVMConstInt(get_llvm_type(g, type_entry), -1, true); + // Safety check: check for overflow (dividend = minInt and divisor = -1) + if (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) { + LLVMValueRef neg_1_value = LLVMConstAllOnes(get_llvm_type(g, operand_type)); BigInt int_min_bi = {0}; - eval_min_max_value_int(g, type_entry, &int_min_bi, false); - LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, type_entry), &int_min_bi); + eval_min_max_value_int(g, scalar_type, &int_min_bi, false); + LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi); + LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); + if (operand_type->id == ZigTypeIdVector) { + overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit); + } LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block); @@ -2669,18 +2702,22 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast } } - if (type_entry->id == ZigTypeIdFloat) { + if (scalar_type->id == ZigTypeIdFloat) { LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); switch (div_kind) { case DivKindFloat: return result; case DivKindExact: if (want_runtime_safety) { - LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); + // Safety check: a / b == floor(a / b) + LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); - + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit); + } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); @@ -2695,54 +2732,61 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero"); LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); + if (operand_type->id == ZigTypeIdVector) { + ltz = scalarize_cmp_result(g, ltz); + } LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); LLVMPositionBuilderAtEnd(g->builder, ltz_block); - LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil); + LLVMValueRef ceiled = gen_float_op(g, result, operand_type, BuiltinFnIdCeil); LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); LLVMBuildBr(g->builder, end_block); LLVMPositionBuilderAtEnd(g->builder, gez_block); - LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor); + LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); LLVMBuildBr(g->builder, end_block); LLVMPositionBuilderAtEnd(g->builder, end_block); - LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, type_entry), ""); + LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, operand_type), ""); LLVMValueRef incoming_values[] = { ceiled, floored }; LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block }; LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); return phi; } case DivKindFloor: - return gen_float_op(g, result, type_entry, BuiltinFnIdFloor); + return gen_float_op(g, result, operand_type, BuiltinFnIdFloor); } zig_unreachable(); } - assert(type_entry->id == ZigTypeIdInt); + assert(scalar_type->id == ZigTypeIdInt); switch (div_kind) { case DivKindFloat: zig_unreachable(); case DivKindTrunc: - if (type_entry->data.integral.is_signed) { + if (scalar_type->data.integral.is_signed) { return LLVMBuildSDiv(g->builder, val1, val2, ""); } else { return LLVMBuildUDiv(g->builder, val1, val2, ""); } case DivKindExact: if (want_runtime_safety) { + // Safety check: a % b == 0 LLVMValueRef remainder_val; - if (type_entry->data.integral.is_signed) { + if (scalar_type->data.integral.is_signed) { remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); } else { remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); } - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit); + } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); @@ -2750,14 +2794,14 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMPositionBuilderAtEnd(g->builder, ok_block); } - if (type_entry->data.integral.is_signed) { + if (scalar_type->data.integral.is_signed) { return LLVMBuildExactSDiv(g->builder, val1, val2, ""); } else { return LLVMBuildExactUDiv(g->builder, val1, val2, ""); } case DivKindFloor: { - if (!type_entry->data.integral.is_signed) { + if (!scalar_type->data.integral.is_signed) { return LLVMBuildUDiv(g->builder, val1, val2, ""); } // const d = @divTrunc(a, b); @@ -2784,22 +2828,30 @@ enum RemKind { }; static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math, - LLVMValueRef val1, LLVMValueRef val2, - ZigType *type_entry, RemKind rem_kind) + LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind) { + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + ZigLLVMSetFastMath(g->builder, want_fast_math); - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, type_entry)); + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); if (want_runtime_safety) { + // Safety check: divisor != 0 LLVMValueRef is_zero_bit; - if (type_entry->id == ZigTypeIdInt) { - LLVMIntPredicate pred = type_entry->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; + if (scalar_type->id == ZigTypeIdInt) { + LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, ""); - } else if (type_entry->id == ZigTypeIdFloat) { + } else if (scalar_type->id == ZigTypeIdFloat) { is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); } else { zig_unreachable(); } + + if (operand_type->id == ZigTypeIdVector) { + is_zero_bit = scalarize_cmp_result(g, is_zero_bit); + } + LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block); @@ -2810,7 +2862,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block); } - if (type_entry->id == ZigTypeIdFloat) { + if (scalar_type->id == ZigTypeIdFloat) { if (rem_kind == RemKindRem) { return LLVMBuildFRem(g->builder, val1, val2, ""); } else { @@ -2821,8 +2873,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast return LLVMBuildSelect(g->builder, ltz, c, a, ""); } } else { - assert(type_entry->id == ZigTypeIdInt); - if (type_entry->data.integral.is_signed) { + assert(scalar_type->id == ZigTypeIdInt); + if (scalar_type->data.integral.is_signed) { if (rem_kind == RemKindRem) { return LLVMBuildSRem(g->builder, val1, val2, ""); } else { @@ -3010,22 +3062,22 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, } case IrBinOpDivUnspecified: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, scalar_type, DivKindFloat); + op1_value, op2_value, operand_type, DivKindFloat); case IrBinOpDivExact: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, scalar_type, DivKindExact); + op1_value, op2_value, operand_type, DivKindExact); case IrBinOpDivTrunc: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, scalar_type, DivKindTrunc); + op1_value, op2_value, operand_type, DivKindTrunc); case IrBinOpDivFloor: return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, scalar_type, DivKindFloor); + op1_value, op2_value, operand_type, DivKindFloor); case IrBinOpRemRem: return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, scalar_type, RemKindRem); + op1_value, op2_value, operand_type, RemKindRem); case IrBinOpRemMod: return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, scalar_type, RemKindMod); + op1_value, op2_value, operand_type, RemKindMod); } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index bc222a311b..04eaa217b9 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16943,6 +16943,7 @@ static bool ok_float_op(IrBinOp op) { case IrBinOpDivExact: case IrBinOpRemRem: case IrBinOpRemMod: + case IrBinOpRemUnspecified: return true; case IrBinOpBoolOr: @@ -16963,7 +16964,6 @@ static bool ok_float_op(IrBinOp op) { case IrBinOpAddWrap: case IrBinOpSubWrap: case IrBinOpMultWrap: - case IrBinOpRemUnspecified: case IrBinOpArrayCat: case IrBinOpArrayMult: return false; @@ -16991,6 +16991,31 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { zig_unreachable(); } +static bool value_cmp_zero_any(ZigValue *value, Cmp predicate) { + assert(value->special == ConstValSpecialStatic); + + switch (value->type->id) { + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: + return bigint_cmp_zero(&value->data.x_bigint) == predicate; + case ZigTypeIdComptimeFloat: + case ZigTypeIdFloat: + if (float_is_nan(value)) + return false; + return float_cmp_zero(value) == predicate; + case ZigTypeIdVector: { + for (size_t i = 0; i < value->type->data.vector.len; i++) { + ZigValue *scalar_val = &value->data.x_array.data.s_none.elements[i]; + if (!value_cmp_zero_any(scalar_val, predicate)) + return true; + } + return false; + } + default: + zig_unreachable(); + } +} + static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { Error err; @@ -17096,127 +17121,13 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc if (type_is_invalid(resolved_type)) return ira->codegen->invalid_inst_gen; - bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt; - bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat; - bool is_signed_div = ( - (resolved_type->id == ZigTypeIdInt && resolved_type->data.integral.is_signed) || - resolved_type->id == ZigTypeIdFloat || - (resolved_type->id == ZigTypeIdComptimeFloat && - ((bigfloat_cmp_zero(&op1->value->data.x_bigfloat) != CmpGT) != - (bigfloat_cmp_zero(&op2->value->data.x_bigfloat) != CmpGT))) || - (resolved_type->id == ZigTypeIdComptimeInt && - ((bigint_cmp_zero(&op1->value->data.x_bigint) != CmpGT) != - (bigint_cmp_zero(&op2->value->data.x_bigint) != CmpGT))) - ); - if (op_id == IrBinOpDivUnspecified && is_int) { - if (is_signed_div) { - bool ok = false; - if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; + ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ? + resolved_type->data.vector.elem_type : resolved_type; - ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; + bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt; + bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat; - if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) { - // the division by zero error will be caught later, but we don't have a - // division function ambiguity problem. - op_id = IrBinOpDivTrunc; - ok = true; - } else { - BigInt trunc_result; - BigInt floor_result; - bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) { - ok = true; - op_id = IrBinOpDivTrunc; - } - } - } - if (!ok) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - } else { - op_id = IrBinOpDivTrunc; - } - } else if (op_id == IrBinOpRemUnspecified) { - if (is_signed_div && (is_int || is_float)) { - bool ok = false; - if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (is_int) { - ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (bigint_cmp_zero(&op2->value->data.x_bigint) == CmpEQ) { - // the division by zero error will be caught later, but we don't - // have a remainder function ambiguity problem - ok = true; - } else { - BigInt rem_result; - BigInt mod_result; - bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ; - } - } else { - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (float_cmp_zero(casted_op2->value) == CmpEQ) { - // the division by zero error will be caught later, but we don't - // have a remainder function ambiguity problem - ok = true; - } else { - ZigValue rem_result = {}; - ZigValue mod_result = {}; - float_rem(&rem_result, op1_val, op2_val); - float_mod(&mod_result, op1_val, op2_val); - ok = float_cmp(&rem_result, &mod_result) == CmpEQ; - } - } - } - if (!ok) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - } - op_id = IrBinOpRemRem; - } - - bool ok = false; - if (is_int) { - ok = true; - } else if (is_float && ok_float_op(op_id)) { - ok = true; - } else if (resolved_type->id == ZigTypeIdVector) { - ZigType *elem_type = resolved_type->data.vector.elem_type; - if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) { - ok = true; - } else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) { - ok = true; - } - } - if (!ok) { + if (!is_int && !(is_float && ok_float_op(op_id))) { AstNode *source_node = instruction->base.base.source_node; ir_add_error_node(ira, source_node, buf_sprintf("invalid operands to binary expression: '%s' and '%s'", @@ -17225,7 +17136,16 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc return ira->codegen->invalid_inst_gen; } - if (resolved_type->id == ZigTypeIdComptimeInt) { + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + // Comptime integers have no fixed size + if (scalar_type->id == ZigTypeIdComptimeInt) { if (op_id == IrBinOpAddWrap) { op_id = IrBinOpAdd; } else if (op_id == IrBinOpSubWrap) { @@ -17235,25 +17155,131 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc } } - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); - if (type_is_invalid(casted_op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); if (op1_val == nullptr) return ira->codegen->invalid_inst_gen; + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; + // Promote division with negative numbers to signed + bool is_signed_div = value_cmp_zero_any(op1_val, CmpLT) || + value_cmp_zero_any(op2_val, CmpLT); + + if (op_id == IrBinOpDivUnspecified && is_int) { + // Default to truncating division and check if it's valid for the + // given operands if signed + op_id = IrBinOpDivTrunc; + + if (is_signed_div) { + bool ok = false; + + if (value_cmp_zero_any(op2_val, CmpEQ)) { + // the division by zero error will be caught later, but we don't have a + // division function ambiguity problem. + ok = true; + } else { + IrInstGen *trunc_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpDivTrunc, op2_val); + if (type_is_invalid(trunc_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *floor_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpDivFloor, op2_val); + if (type_is_invalid(floor_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, + trunc_val, floor_val, IrBinOpCmpEq); + if (type_is_invalid(cmp_val->value->type)) + return ira->codegen->invalid_inst_gen; + + // We can "upgrade" the operator only if trunc(a/b) == floor(a/b) + if (!ir_resolve_bool(ira, cmp_val, &ok)) + return ira->codegen->invalid_inst_gen; + } + + if (!ok) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } + } else if (op_id == IrBinOpRemUnspecified) { + op_id = IrBinOpRemRem; + + if (is_signed_div) { + bool ok = false; + + if (value_cmp_zero_any(op2_val, CmpEQ)) { + // the division by zero error will be caught later, but we don't have a + // division function ambiguity problem. + ok = true; + } else { + IrInstGen *rem_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpRemRem, op2_val); + if (type_is_invalid(rem_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *mod_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpRemMod, op2_val); + if (type_is_invalid(mod_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, + rem_val, mod_val, IrBinOpCmpEq); + if (type_is_invalid(cmp_val->value->type)) + return ira->codegen->invalid_inst_gen; + + // We can "upgrade" the operator only if mod(a,b) == rem(a,b) + if (!ir_resolve_bool(ira, cmp_val, &ok)) + return ira->codegen->invalid_inst_gen; + } + + if (!ok) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } + } + return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val); } + const bool is_signed_div = + (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || + scalar_type->id == ZigTypeIdFloat; + + // Warn the user to use the proper operators here + if (op_id == IrBinOpDivUnspecified && is_int) { + op_id = IrBinOpDivTrunc; + + if (is_signed_div) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } else if (op_id == IrBinOpRemUnspecified) { + op_id = IrBinOpRemRem; + + if (is_signed_div) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } + return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, op_id, casted_op1, casted_op2, instruction->safety_check_on); } diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index 01e5ac1fb8..6db695bfa3 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -276,3 +276,78 @@ test "vector comparison operators" { S.doTheTest(); comptime S.doTheTest(); } + +test "vector division operators" { + const S = struct { + fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void { + if (!comptime std.meta.trait.isSignedInt(T)) { + const d0 = x / y; + for (@as([4]T, d0)) |v, i| { + expectEqual(x[i] / y[i], v); + } + } + const d1 = @divExact(x, y); + for (@as([4]T, d1)) |v, i| { + expectEqual(@divExact(x[i], y[i]), v); + } + const d2 = @divFloor(x, y); + for (@as([4]T, d2)) |v, i| { + expectEqual(@divFloor(x[i], y[i]), v); + } + const d3 = @divTrunc(x, y); + for (@as([4]T, d3)) |v, i| { + expectEqual(@divTrunc(x[i], y[i]), v); + } + } + + fn doTheTestMod(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) void { + if ((!comptime std.meta.trait.isSignedInt(T)) and @typeInfo(T) != .Float) { + const r0 = x % y; + for (@as([4]T, r0)) |v, i| { + expectEqual(x[i] % y[i], v); + } + } + const r1 = @mod(x, y); + for (@as([4]T, r1)) |v, i| { + expectEqual(@mod(x[i], y[i]), v); + } + const r2 = @rem(x, y); + for (@as([4]T, r2)) |v, i| { + expectEqual(@rem(x[i], y[i]), v); + } + } + + fn doTheTest() void { + doTheTestDiv(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, -1.0, -2.0 }); + doTheTestDiv(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, -1.0, -2.0 }); + doTheTestDiv(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, -1.0, -2.0 }); + + doTheTestMod(f16, [4]f16{ 4.0, -4.0, 4.0, -4.0 }, [4]f16{ 1.0, 2.0, 0.5, 3.0 }); + doTheTestMod(f32, [4]f32{ 4.0, -4.0, 4.0, -4.0 }, [4]f32{ 1.0, 2.0, 0.5, 3.0 }); + doTheTestMod(f64, [4]f64{ 4.0, -4.0, 4.0, -4.0 }, [4]f64{ 1.0, 2.0, 0.5, 3.0 }); + + doTheTestDiv(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, -1, -2 }); + doTheTestDiv(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, -1, -2 }); + doTheTestDiv(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, -1, -2 }); + doTheTestDiv(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, -1, -2 }); + + doTheTestMod(i8, [4]i8{ 4, -4, 4, -4 }, [4]i8{ 1, 2, 4, 8 }); + doTheTestMod(i16, [4]i16{ 4, -4, 4, -4 }, [4]i16{ 1, 2, 4, 8 }); + doTheTestMod(i32, [4]i32{ 4, -4, 4, -4 }, [4]i32{ 1, 2, 4, 8 }); + doTheTestMod(i64, [4]i64{ 4, -4, 4, -4 }, [4]i64{ 1, 2, 4, 8 }); + + doTheTestDiv(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 }); + doTheTestDiv(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); + doTheTestDiv(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); + doTheTestDiv(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); + + doTheTestMod(u8, [4]u8{ 1, 2, 4, 8 }, [4]u8{ 1, 1, 2, 4 }); + doTheTestMod(u16, [4]u16{ 1, 2, 4, 8 }, [4]u16{ 1, 1, 2, 4 }); + doTheTestMod(u32, [4]u32{ 1, 2, 4, 8 }, [4]u32{ 1, 1, 2, 4 }); + doTheTestMod(u64, [4]u64{ 1, 2, 4, 8 }, [4]u64{ 1, 1, 2, 4 }); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} -- cgit v1.2.3 From d2d97e55ccd2d7c992d01bd05ea52a52fe36776e Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 14 Mar 2020 20:01:28 +0100 Subject: ir: Support shift left/right on vectors --- src/codegen.cpp | 50 ++++++++++++------ src/ir.cpp | 114 +++++++++++++++++++++++++++++----------- test/stage1/behavior/vector.zig | 65 +++++++++++++++++++++++ 3 files changed, 182 insertions(+), 47 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 0fa181b32c..97d960b523 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -155,6 +155,7 @@ static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr, LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, LLVMValueRef result_loc, bool non_async); static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix); +static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); @@ -2535,19 +2536,21 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir return nullptr; } -static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, - LLVMValueRef val1, LLVMValueRef val2) +static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, + LLVMValueRef val1, LLVMValueRef val2) { // for unsigned left shifting, we do the lossy shift, then logically shift // right the same number of bits // if the values don't match, we have an overflow // for signed left shifting we do the same except arithmetic shift right + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; - assert(type_entry->id == ZigTypeIdInt); + assert(scalar_type->id == ZigTypeIdInt); LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); LLVMValueRef orig_val; - if (type_entry->data.integral.is_signed) { + if (scalar_type->data.integral.is_signed) { orig_val = LLVMBuildAShr(g->builder, result, val2, ""); } else { orig_val = LLVMBuildLShr(g->builder, result, val2, ""); @@ -2556,6 +2559,9 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit); + } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); @@ -2565,13 +2571,16 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *type_entry, return result; } -static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, - LLVMValueRef val1, LLVMValueRef val2) +static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, + LLVMValueRef val1, LLVMValueRef val2) { - assert(type_entry->id == ZigTypeIdInt); + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + + assert(scalar_type->id == ZigTypeIdInt); LLVMValueRef result; - if (type_entry->data.integral.is_signed) { + if (scalar_type->data.integral.is_signed) { result = LLVMBuildAShr(g->builder, val1, val2, ""); } else { result = LLVMBuildLShr(g->builder, val1, val2, ""); @@ -2581,6 +2590,9 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry, LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit); + } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); @@ -2897,11 +2909,17 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type // otherwise the check is useful as the allowed values are limited by the // operand type itself if (!is_power_of_2(lhs_type->data.integral.bit_count)) { - LLVMValueRef bit_count_value = LLVMConstInt(get_llvm_type(g, rhs_type), - lhs_type->data.integral.bit_count, false); - LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); + BigInt bit_count_bi = {0}; + bigint_init_unsigned(&bit_count_bi, lhs_type->data.integral.bit_count); + LLVMValueRef bit_count_value = bigint_to_llvm_const(get_llvm_type(g, rhs_type), + &bit_count_bi); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail"); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); + LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); + if (rhs_type->id == ZigTypeIdVector) { + less_than_bit = scalarize_cmp_result(g, less_than_bit); + } LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block); @@ -3018,7 +3036,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, case IrBinOpBitShiftLeftExact: { assert(scalar_type->id == ZigTypeIdInt); - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); + LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, + LLVMTypeOf(op1_value), "");//gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); if (want_runtime_safety) { gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); @@ -3028,7 +3047,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, if (is_sloppy) { return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); } else if (want_runtime_safety) { - return gen_overflow_shl_op(g, scalar_type, op1_value, op2_casted); + return gen_overflow_shl_op(g, operand_type, op1_value, op2_casted); } else if (scalar_type->data.integral.is_signed) { return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); } else { @@ -3039,7 +3058,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, case IrBinOpBitShiftRightExact: { assert(scalar_type->id == ZigTypeIdInt); - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); + LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, + LLVMTypeOf(op1_value), "");//gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); if (want_runtime_safety) { gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); @@ -3053,7 +3073,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); } } else if (want_runtime_safety) { - return gen_overflow_shr_op(g, scalar_type, op1_value, op2_casted); + return gen_overflow_shr_op(g, operand_type, op1_value, op2_casted); } else if (scalar_type->data.integral.is_signed) { return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); } else { diff --git a/src/ir.cpp b/src/ir.cpp index 436db592f2..6fed044c6c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -283,6 +283,8 @@ static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instructi IrInstGen *result_loc); static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, IrInstGen *struct_operand, TypeStructField *field); +static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); +static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); static void destroy_instruction_src(IrInstSrc *inst) { switch (inst->id) { @@ -16803,7 +16805,6 @@ static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr, ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; assert(scalar_op1_val->type == scalar_type); - assert(scalar_op2_val->type == scalar_type); assert(scalar_out_val->type == scalar_type); ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); @@ -16828,27 +16829,49 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in if (type_is_invalid(op1->value->type)) return ira->codegen->invalid_inst_gen; - if (op1->value->type->id != ZigTypeIdInt && op1->value->type->id != ZigTypeIdComptimeInt) { + IrInstGen *op2 = bin_op_instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *op1_type = op1->value->type; + ZigType *op2_type = op2->value->type; + + if (op1_type->id == ZigTypeIdVector && op2_type->id != ZigTypeIdVector) { ir_add_error(ira, &bin_op_instruction->op1->base, - buf_sprintf("bit shifting operation expected integer type, found '%s'", - buf_ptr(&op1->value->type->name))); + buf_sprintf("bit shifting operation expected vector type, found '%s'", + buf_ptr(&op2_type->name))); return ira->codegen->invalid_inst_gen; } - IrInstGen *op2 = bin_op_instruction->op2->child; - if (type_is_invalid(op2->value->type)) + if (op1_type->id != ZigTypeIdVector && op2_type->id == ZigTypeIdVector) { + ir_add_error(ira, &bin_op_instruction->op1->base, + buf_sprintf("bit shifting operation expected vector type, found '%s'", + buf_ptr(&op1_type->name))); return ira->codegen->invalid_inst_gen; + } + + ZigType *op1_scalar_type = (op1_type->id == ZigTypeIdVector) ? + op1_type->data.vector.elem_type : op1_type; + ZigType *op2_scalar_type = (op2_type->id == ZigTypeIdVector) ? + op2_type->data.vector.elem_type : op2_type; + + if (op1_scalar_type->id != ZigTypeIdInt && op1_scalar_type->id != ZigTypeIdComptimeInt) { + ir_add_error(ira, &bin_op_instruction->op1->base, + buf_sprintf("bit shifting operation expected integer type, found '%s'", + buf_ptr(&op1_scalar_type->name))); + return ira->codegen->invalid_inst_gen; + } - if (op2->value->type->id != ZigTypeIdInt && op2->value->type->id != ZigTypeIdComptimeInt) { + if (op2_scalar_type->id != ZigTypeIdInt && op2_scalar_type->id != ZigTypeIdComptimeInt) { ir_add_error(ira, &bin_op_instruction->op2->base, buf_sprintf("shift amount has to be an integer type, but found '%s'", - buf_ptr(&op2->value->type->name))); + buf_ptr(&op2_scalar_type->name))); return ira->codegen->invalid_inst_gen; } IrInstGen *casted_op2; IrBinOp op_id = bin_op_instruction->op_id; - if (op1->value->type->id == ZigTypeIdComptimeInt) { + if (op1_scalar_type->id == ZigTypeIdComptimeInt) { // comptime_int has no finite bit width casted_op2 = op2; @@ -16874,10 +16897,15 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in return ira->codegen->invalid_inst_gen; } } else { - const unsigned bit_count = op1->value->type->data.integral.bit_count; + const unsigned bit_count = op1_scalar_type->data.integral.bit_count; ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, bit_count > 0 ? bit_count - 1 : 0); + if (op1_type->id == ZigTypeIdVector) { + shift_amt_type = get_vector_type(ira->codegen, op1_type->data.vector.len, + shift_amt_type); + } + casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); if (type_is_invalid(casted_op2->value->type)) return ira->codegen->invalid_inst_gen; @@ -16888,10 +16916,10 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; - BigInt bit_count_value = {0}; - bigint_init_unsigned(&bit_count_value, bit_count); + ZigValue bit_count_value; + init_const_usize(ira->codegen, &bit_count_value, bit_count); - if (bigint_cmp(&op2_val->data.x_bigint, &bit_count_value) != CmpLT) { + if (!value_cmp_numeric_val_all(op2_val, CmpLT, &bit_count_value)) { ErrorMsg* msg = ir_add_error(ira, &bin_op_instruction->base.base, buf_sprintf("RHS of shift is too large for LHS type")); @@ -16910,7 +16938,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; - if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) + if (value_cmp_numeric_val_all(op2_val, CmpEQ, nullptr)) return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1); } @@ -16923,7 +16951,7 @@ static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_in if (op2_val == nullptr) return ira->codegen->invalid_inst_gen; - return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1->value->type, op1_val, op_id, op2_val); + return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1_type, op1_val, op_id, op2_val); } return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, @@ -16991,31 +17019,53 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { zig_unreachable(); } -static bool value_cmp_zero_any(ZigValue *value, Cmp predicate) { - assert(value->special == ConstValSpecialStatic); +static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { + assert(left->special == ConstValSpecialStatic); + assert(right == nullptr || right->special == ConstValSpecialStatic); - switch (value->type->id) { + switch (left->type->id) { case ZigTypeIdComptimeInt: - case ZigTypeIdInt: - return bigint_cmp_zero(&value->data.x_bigint) == predicate; + case ZigTypeIdInt: { + const Cmp result = right ? + bigint_cmp(&left->data.x_bigint, &right->data.x_bigint) : + bigint_cmp_zero(&left->data.x_bigint); + return result == predicate; + } case ZigTypeIdComptimeFloat: - case ZigTypeIdFloat: - if (float_is_nan(value)) + case ZigTypeIdFloat: { + if (float_is_nan(left)) return false; - return float_cmp_zero(value) == predicate; + if (right != nullptr && float_is_nan(right)) + return false; + + const Cmp result = right ? float_cmp(left, right) : float_cmp_zero(left); + return result == predicate; + } case ZigTypeIdVector: { - for (size_t i = 0; i < value->type->data.vector.len; i++) { - ZigValue *scalar_val = &value->data.x_array.data.s_none.elements[i]; - if (!value_cmp_zero_any(scalar_val, predicate)) - return true; + for (size_t i = 0; i < left->type->data.vector.len; i++) { + ZigValue *scalar_val = &left->data.x_array.data.s_none.elements[i]; + const bool result = value_cmp_numeric_val(scalar_val, predicate, right, any); + + if (any && result) + return true; // This element satisfies the predicate + else if (!any && !result) + return false; // This element doesn't satisfy the predicate } - return false; + return any ? false : true; } default: zig_unreachable(); } } +static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right) { + return value_cmp_numeric_val(left, predicate, right, true); +} + +static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right) { + return value_cmp_numeric_val(left, predicate, right, false); +} + static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { Error err; @@ -17165,8 +17215,8 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc return ira->codegen->invalid_inst_gen; // Promote division with negative numbers to signed - bool is_signed_div = value_cmp_zero_any(op1_val, CmpLT) || - value_cmp_zero_any(op2_val, CmpLT); + bool is_signed_div = value_cmp_numeric_val_any(op1_val, CmpLT, nullptr) || + value_cmp_numeric_val_any(op2_val, CmpLT, nullptr); if (op_id == IrBinOpDivUnspecified && is_int) { // Default to truncating division and check if it's valid for the @@ -17176,7 +17226,7 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc if (is_signed_div) { bool ok = false; - if (value_cmp_zero_any(op2_val, CmpEQ)) { + if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { // the division by zero error will be caught later, but we don't have a // division function ambiguity problem. ok = true; @@ -17215,7 +17265,7 @@ static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruc if (is_signed_div) { bool ok = false; - if (value_cmp_zero_any(op2_val, CmpEQ)) { + if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { // the division by zero error will be caught later, but we don't have a // division function ambiguity problem. ok = true; diff --git a/test/stage1/behavior/vector.zig b/test/stage1/behavior/vector.zig index f242aa0fbf..f3bc334b84 100644 --- a/test/stage1/behavior/vector.zig +++ b/test/stage1/behavior/vector.zig @@ -1,5 +1,6 @@ const std = @import("std"); const mem = std.mem; +const math = std.math; const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; @@ -376,3 +377,67 @@ test "vector bitwise not operator" { S.doTheTest(); comptime S.doTheTest(); } + +test "vector shift operators" { + const S = struct { + fn doTheTestShift(x: var, y: var) void { + const N = @typeInfo(@TypeOf(x)).Array.len; + const TX = @typeInfo(@TypeOf(x)).Array.child; + const TY = @typeInfo(@TypeOf(y)).Array.child; + + var xv = @as(@Vector(N, TX), x); + var yv = @as(@Vector(N, TY), y); + + var z0 = xv >> yv; + for (@as([N]TX, z0)) |v, i| { + expectEqual(x[i] >> y[i], v); + } + var z1 = xv << yv; + for (@as([N]TX, z1)) |v, i| { + expectEqual(x[i] << y[i], v); + } + } + fn doTheTestShiftExact(x: var, y: var, dir: enum { Left, Right }) void { + const N = @typeInfo(@TypeOf(x)).Array.len; + const TX = @typeInfo(@TypeOf(x)).Array.child; + const TY = @typeInfo(@TypeOf(y)).Array.child; + + var xv = @as(@Vector(N, TX), x); + var yv = @as(@Vector(N, TY), y); + + var z = if (dir == .Left) @shlExact(xv, yv) else @shrExact(xv, yv); + for (@as([N]TX, z)) |v, i| { + const check = if (dir == .Left) x[i] << y[i] else x[i] >> y[i]; + expectEqual(check, v); + } + } + fn doTheTest() void { + doTheTestShift([_]u8{ 0, 2, 4, math.maxInt(u8) }, [_]u3{ 2, 0, 2, 7 }); + doTheTestShift([_]u16{ 0, 2, 4, math.maxInt(u16) }, [_]u4{ 2, 0, 2, 15 }); + doTheTestShift([_]u24{ 0, 2, 4, math.maxInt(u24) }, [_]u5{ 2, 0, 2, 23 }); + doTheTestShift([_]u32{ 0, 2, 4, math.maxInt(u32) }, [_]u5{ 2, 0, 2, 31 }); + doTheTestShift([_]u64{ 0xfe, math.maxInt(u64) }, [_]u6{ 0, 63 }); + + doTheTestShift([_]i8{ 0, 2, 4, math.maxInt(i8) }, [_]u3{ 2, 0, 2, 7 }); + doTheTestShift([_]i16{ 0, 2, 4, math.maxInt(i16) }, [_]u4{ 2, 0, 2, 7 }); + doTheTestShift([_]i24{ 0, 2, 4, math.maxInt(i24) }, [_]u5{ 2, 0, 2, 7 }); + doTheTestShift([_]i32{ 0, 2, 4, math.maxInt(i32) }, [_]u5{ 2, 0, 2, 7 }); + doTheTestShift([_]i64{ 0xfe, math.maxInt(i64) }, [_]u6{ 0, 63 }); + + doTheTestShiftExact([_]u8{ 0, 1, 1 << 7, math.maxInt(u8) ^ 1 }, [_]u3{ 4, 0, 7, 1 }, .Right); + doTheTestShiftExact([_]u16{ 0, 1, 1 << 15, math.maxInt(u16) ^ 1 }, [_]u4{ 4, 0, 15, 1 }, .Right); + doTheTestShiftExact([_]u24{ 0, 1, 1 << 23, math.maxInt(u24) ^ 1 }, [_]u5{ 4, 0, 23, 1 }, .Right); + doTheTestShiftExact([_]u32{ 0, 1, 1 << 31, math.maxInt(u32) ^ 1 }, [_]u5{ 4, 0, 31, 1 }, .Right); + doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 63, 0 }, .Right); + + doTheTestShiftExact([_]u8{ 0, 1, 1, math.maxInt(u8) ^ (1 << 7) }, [_]u3{ 4, 0, 7, 1 }, .Left); + doTheTestShiftExact([_]u16{ 0, 1, 1, math.maxInt(u16) ^ (1 << 15) }, [_]u4{ 4, 0, 15, 1 }, .Left); + doTheTestShiftExact([_]u24{ 0, 1, 1, math.maxInt(u24) ^ (1 << 23) }, [_]u5{ 4, 0, 23, 1 }, .Left); + doTheTestShiftExact([_]u32{ 0, 1, 1, math.maxInt(u32) ^ (1 << 31) }, [_]u5{ 4, 0, 31, 1 }, .Left); + doTheTestShiftExact([_]u64{ 1 << 63, 1 }, [_]u6{ 0, 63 }, .Left); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} -- cgit v1.2.3 From fe77c38247a8869719579f911bf2775f00462330 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 24 Mar 2020 18:58:53 +0100 Subject: ir: Remove unused and commented out code --- src/codegen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 97d960b523..e7fff882c3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3037,7 +3037,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, { assert(scalar_type->id == ZigTypeIdInt); LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, - LLVMTypeOf(op1_value), "");//gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); + LLVMTypeOf(op1_value), ""); if (want_runtime_safety) { gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); @@ -3059,7 +3059,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, { assert(scalar_type->id == ZigTypeIdInt); LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, - LLVMTypeOf(op1_value), "");//gen_widen_or_shorten(g, false, op2->value->type, scalar_type, op2_value); + LLVMTypeOf(op1_value), ""); if (want_runtime_safety) { gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); -- cgit v1.2.3 From f6cdc94a50235eaf145f6c2c2ec257008d592494 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 5 Apr 2020 10:40:41 +0200 Subject: ir: Fix error checking for vector ops The extra logic that's needed was lost during a refactoring, now it should be fine. --- src/codegen.cpp | 59 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 20 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index e7fff882c3..a2cd5fafc0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -155,7 +155,6 @@ static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr, LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, LLVMValueRef result_loc, bool non_async); static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix); -static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); @@ -2536,6 +2535,36 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, Ir return nullptr; } +enum class ScalarizePredicate { + // Returns true iff all the elements in the vector are 1. + // Equivalent to folding all the bits with `and`. + All, + // Returns true iff there's at least one element in the vector that is 1. + // Equivalent to folding all the bits with `or`. + Any, +}; + +// Collapses a vector into a single i1 according to the given predicate +static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) { + assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); + LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); + LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); + + switch (predicate) { + case ScalarizePredicate::Any: { + LLVMValueRef all_zeros = LLVMConstNull(scalar_type); + return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, ""); + } + case ScalarizePredicate::All: { + LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); + return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); + } + } + + zig_unreachable(); +} + + static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, LLVMValueRef val1, LLVMValueRef val2) { @@ -2560,7 +2589,7 @@ static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit); + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); @@ -2591,7 +2620,7 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit); + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); @@ -2647,16 +2676,6 @@ static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { } } -// Collapses a vector into a single i1 whose value is 1 iff all the -// vector elements are 1 -static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val) { - assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); - LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); - LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); - LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); - return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); -} - static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math, LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind) { @@ -2678,7 +2697,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast } if (operand_type->id == ZigTypeIdVector) { - is_zero_bit = scalarize_cmp_result(g, is_zero_bit); + is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); } LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); @@ -2703,7 +2722,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); if (operand_type->id == ZigTypeIdVector) { - overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit); + overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any); } LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); @@ -2728,7 +2747,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit); + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); @@ -2745,7 +2764,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); if (operand_type->id == ZigTypeIdVector) { - ltz = scalarize_cmp_result(g, ltz); + ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any); } LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); @@ -2797,7 +2816,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit); + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); } LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); @@ -2861,7 +2880,7 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast } if (operand_type->id == ZigTypeIdVector) { - is_zero_bit = scalarize_cmp_result(g, is_zero_bit); + is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); } LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); @@ -2918,7 +2937,7 @@ static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); if (rhs_type->id == ZigTypeIdVector) { - less_than_bit = scalarize_cmp_result(g, less_than_bit); + less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any); } LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); -- cgit v1.2.3 From 64d0960244a219526fc100b17f4ecd26223df496 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 6 Apr 2020 19:13:36 -0400 Subject: zig cc: recognize a few more linker options * `--major-image-version` * `--minor-image-version` * `--stack` --- src/all_types.hpp | 1 + src/codegen.cpp | 1 + src/link.cpp | 6 ++++-- src/main.cpp | 23 +++++++++++++++++++++++ 4 files changed, 29 insertions(+), 2 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index 9b22279b91..650dcfd0c7 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2259,6 +2259,7 @@ struct CodeGen { size_t version_minor; size_t version_patch; const char *linker_script; + size_t stack_size_override; BuildMode build_mode; OutType out_type; diff --git a/src/codegen.cpp b/src/codegen.cpp index a2cd5fafc0..7de26e0b6d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -10597,6 +10597,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_int(ch, g->linker_allow_shlib_undefined); cache_bool(ch, g->linker_z_nodelete); cache_bool(ch, g->linker_z_defs); + cache_usize(ch, g->stack_size_override); // gen_c_objects appends objects to g->link_objects which we want to include in the hash gen_c_objects(g); diff --git a/src/link.cpp b/src/link.cpp index 4ee8915e55..439b76a756 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -1840,7 +1840,8 @@ static void construct_linker_job_elf(LinkJob *lj) { if (g->out_type == OutTypeExe) { lj->args.append("-z"); - lj->args.append("stack-size=16777216"); // default to 16 MiB + size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override; + lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size))); } if (g->linker_script) { @@ -2479,7 +2480,8 @@ static void construct_linker_job_coff(LinkJob *lj) { if (g->out_type == OutTypeExe) { // TODO compile time stack upper bound detection - lj->args.append("-STACK:16777216"); + size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override; + lj->args.append(buf_ptr(buf_sprintf("-STACK:%" ZIG_PRI_usize, stack_size))); } coff_append_machine_arg(g, &lj->args); diff --git a/src/main.cpp b/src/main.cpp index a2d7577a74..8805382f37 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -453,6 +453,7 @@ static int main0(int argc, char **argv) { OptionalBool linker_allow_shlib_undefined = OptionalBoolNull; bool linker_z_nodelete = false; bool linker_z_defs = false; + size_t stack_size_override = 0; ZigList llvm_argv = {0}; llvm_argv.append("zig (LLVM option parsing)"); @@ -848,6 +849,27 @@ static int main0(int argc, char **argv) { } else { fprintf(stderr, "warning: unsupported linker arg: -z %s\n", buf_ptr(z_arg)); } + } else if (buf_eql_str(arg, "--major-image-version")) { + i += 1; + if (i >= linker_args.length) { + fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); + return EXIT_FAILURE; + } + ver_major = atoi(buf_ptr(linker_args.at(i))); + } else if (buf_eql_str(arg, "--minor-image-version")) { + i += 1; + if (i >= linker_args.length) { + fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); + return EXIT_FAILURE; + } + ver_minor = atoi(buf_ptr(linker_args.at(i))); + } else if (buf_eql_str(arg, "--stack")) { + i += 1; + if (i >= linker_args.length) { + fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); + return EXIT_FAILURE; + } + stack_size_override = atoi(buf_ptr(linker_args.at(i))); } else { fprintf(stderr, "warning: unsupported linker arg: %s\n", buf_ptr(arg)); } @@ -1573,6 +1595,7 @@ static int main0(int argc, char **argv) { g->linker_allow_shlib_undefined = linker_allow_shlib_undefined; g->linker_z_nodelete = linker_z_nodelete; g->linker_z_defs = linker_z_defs; + g->stack_size_override = stack_size_override; if (override_soname) { g->override_soname = buf_create_from_str(override_soname); -- cgit v1.2.3 From cc0fca9d83f5a62cf0e109dde3a323c01ea71301 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 7 Apr 2020 10:23:51 +0200 Subject: stage1: Respect the specified name for extern var Extend the logic used for function definitions to variables. Closes #4947 --- src/all_types.hpp | 2 +- src/analyze.cpp | 6 +++++- src/codegen.cpp | 27 +++++++++++++-------------- 3 files changed, 19 insertions(+), 16 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index 56ce74caae..db745275f7 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -2083,7 +2083,7 @@ struct CodeGen { HashMap memoized_fn_eval_table; HashMap llvm_fn_table; HashMap exported_symbol_names; - HashMap external_prototypes; + HashMap external_symbol_names; HashMap string_literals_table; HashMap type_info_cache; HashMap one_possible_values; diff --git a/src/analyze.cpp b/src/analyze.cpp index 14b9c25c07..c3580e35ea 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3498,7 +3498,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { } } else { fn_table_entry->inferred_async_node = inferred_async_none; - g->external_prototypes.put_unique(tld_fn->base.name, &tld_fn->base); + g->external_symbol_names.put_unique(tld_fn->base.name, &tld_fn->base); } Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; @@ -4048,6 +4048,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong); } + if (is_extern) { + g->external_symbol_names.put_unique(tld_var->base.name, &tld_var->base); + } + g->global_vars.append(tld_var); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 7de26e0b6d..980a8a4aca 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -204,15 +204,14 @@ static bool is_symbol_available(CodeGen *g, const char *name) { Buf *buf_name = buf_create_from_str(name); bool result = g->exported_symbol_names.maybe_get(buf_name) == nullptr && - g->external_prototypes.maybe_get(buf_name) == nullptr; + g->external_symbol_names.maybe_get(buf_name) == nullptr; buf_destroy(buf_name); return result; } -static const char *get_mangled_name(CodeGen *g, const char *original_name, bool external_linkage) { - if (external_linkage || is_symbol_available(g, original_name)) { +static const char *get_mangled_name(CodeGen *g, const char *original_name) { + if (is_symbol_available(g, original_name)) return original_name; - } int n = 0; for (;; n += 1) { @@ -437,7 +436,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { symbol_name = unmangled_name; linkage = GlobalLinkageIdStrong; } else if (fn->export_list.length == 0) { - symbol_name = get_mangled_name(g, unmangled_name, false); + symbol_name = get_mangled_name(g, unmangled_name); linkage = GlobalLinkageIdInternal; } else { GlobalExport *fn_export = &fn->export_list.items[0]; @@ -1115,7 +1114,7 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { }; LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); - const char *fn_name = get_mangled_name(g, "__zig_add_err_ret_trace_addr", false); + const char *fn_name = get_mangled_name(g, "__zig_add_err_ret_trace_addr"); LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); addLLVMFnAttr(fn_val, "alwaysinline"); LLVMSetLinkage(fn_val, LLVMInternalLinkage); @@ -1194,7 +1193,7 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { }; LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false); - const char *fn_name = get_mangled_name(g, "__zig_return_error", false); + const char *fn_name = get_mangled_name(g, "__zig_return_error"); LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address addLLVMFnAttr(fn_val, "cold"); @@ -1264,7 +1263,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { LLVMSetLinkage(msg_prefix, LLVMPrivateLinkage); LLVMSetGlobalConstant(msg_prefix, true); - const char *fn_name = get_mangled_name(g, "__zig_fail_unwrap", false); + const char *fn_name = get_mangled_name(g, "__zig_fail_unwrap"); LLVMTypeRef fn_type_ref; if (g->have_err_ret_tracing) { LLVMTypeRef arg_types[] = { @@ -2174,7 +2173,7 @@ static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { }; LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); - const char *fn_name = get_mangled_name(g, "__zig_merge_error_return_traces", false); + const char *fn_name = get_mangled_name(g, "__zig_merge_error_return_traces"); LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); LLVMSetLinkage(fn_val, LLVMInternalLinkage); ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); @@ -5099,7 +5098,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { &tag_int_llvm_type, 1, false); const char *fn_name = get_mangled_name(g, - buf_ptr(buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name))), false); + buf_ptr(buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)))); LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); LLVMSetLinkage(fn_val, LLVMInternalLinkage); ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); @@ -7588,7 +7587,7 @@ static void generate_error_name_table(CodeGen *g) { LLVMValueRef err_name_table_init = LLVMConstArray(get_llvm_type(g, str_type), values, (unsigned)g->errors_by_index.length); g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init), - get_mangled_name(g, buf_ptr(buf_create_from_str("__zig_err_name_table")), false)); + get_mangled_name(g, buf_ptr(buf_create_from_str("__zig_err_name_table")))); LLVMSetInitializer(g->err_name_table, err_name_table_init); LLVMSetLinkage(g->err_name_table, LLVMPrivateLinkage); LLVMSetGlobalConstant(g->err_name_table, true); @@ -7719,7 +7718,7 @@ static void do_code_gen(CodeGen *g) { symbol_name = unmangled_name; linkage = GlobalLinkageIdStrong; } else { - symbol_name = get_mangled_name(g, unmangled_name, false); + symbol_name = get_mangled_name(g, unmangled_name); linkage = GlobalLinkageIdInternal; } } else { @@ -10954,7 +10953,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget g->llvm_fn_table.init(16); g->memoized_fn_eval_table.init(16); g->exported_symbol_names.init(8); - g->external_prototypes.init(8); + g->external_symbol_names.init(8); g->string_literals_table.init(16); g->type_info_cache.init(32); g->one_possible_values.init(32); @@ -10964,7 +10963,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget buf_resize(&g->global_asm, 0); for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { - g->external_prototypes.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); + g->external_symbol_names.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); } if (root_src_path) { -- cgit v1.2.3