From 5683fe3f8c2190f6e6d478836ef4631dca8df9fd Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 11 Jan 2022 22:24:47 +0100 Subject: [PATCH] Add parsing for escaping and remove "fault" token name. Allow excluding stdlib. Fixes to the x64 abi: no narrowing done for i32 results, assuming too many registers, more tests. --- CMakeLists.txt | 3 +- src/build/build_options.c | 5 ++ src/build/build_options.h | 2 + src/build/builder.c | 1 + src/compiler/c_abi_internal.h | 12 ++- src/compiler/codegen_internal.h | 13 +++ src/compiler/compiler_internal.h | 1 + src/compiler/enums.h | 4 +- src/compiler/llvm_codegen_c_abi.c | 10 +++ src/compiler/llvm_codegen_c_abi_aarch64.c | 11 +-- src/compiler/llvm_codegen_c_abi_riscv.c | 10 +-- src/compiler/llvm_codegen_c_abi_win64.c | 2 +- src/compiler/llvm_codegen_c_abi_x64.c | 20 ++--- src/compiler/llvm_codegen_c_abi_x86.c | 9 +- src/compiler/llvm_codegen_function.c | 25 ++++-- src/compiler/llvm_codegen_type.c | 6 +- src/compiler/parse_expr.c | 1 - src/compiler/sema_decls.c | 5 ++ src/compiler/sema_expr.c | 2 + src/compiler/semantic_analyser.c | 2 +- src/compiler/symtab.c | 1 + src/compiler/tb_codegen.c | 1 + src/compiler/tilde_codegen_expr.c | 6 ++ src/compiler/tilde_codegen_type.c | 11 +++ src/compiler/tilde_internal.h | 2 + src/compiler/tokens.c | 2 - src/compiler/types.c | 1 - test/test_suite/abi/darwinx64_1.c3t | 48 +++++++++++ test/test_suite/abi/darwinx64_2.c3t | 93 +++++++++++++++++++++ test/test_suite/abi/small_struct_x64.c3t | 73 ++++++++++++++++ test/test_suite/abi/x64alignarray.c3t | 28 +++++++ test/test_suite/expressions/elvis.c3t | 2 + test/test_suite/expressions/folding_ptr.c3t | 34 ++++++++ 33 files changed, 397 insertions(+), 49 deletions(-) create mode 100644 src/compiler/tilde_codegen_type.c create mode 100644 test/test_suite/abi/darwinx64_1.c3t create mode 100644 test/test_suite/abi/darwinx64_2.c3t create mode 100644 test/test_suite/abi/small_struct_x64.c3t create mode 100644 test/test_suite/abi/x64alignarray.c3t create mode 100644 test/test_suite/expressions/folding_ptr.c3t diff --git a/CMakeLists.txt b/CMakeLists.txt index 81f4abd6b..c4ae5d92e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,7 +194,8 @@ add_executable(c3c src/compiler/tilde_codegen_storeload.c src/compiler/llvm_codegen_storeload.c src/compiler/tilde_codegen_expr.c - src/compiler/tilde_codegen_stmt.c) + src/compiler/tilde_codegen_stmt.c + src/compiler/tilde_codegen_type.c) if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC") message(STATUS "using gcc/clang warning switches") diff --git a/src/build/build_options.c b/src/build/build_options.c index 9f202b082..d8224a4c4 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -496,6 +496,11 @@ static void parse_option(BuildOptions *options) options->std_lib_dir = check_dir(next_arg()); return; } + if (match_longopt("nostdlib")) + { + options->no_stdlib = true; + return; + } if (match_longopt("lib")) { if (at_end() || next_is_opt()) error_exit("error: --lib needs a directory."); diff --git a/src/build/build_options.h b/src/build/build_options.h index 8f35e80de..e34a4527f 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -203,6 +203,7 @@ typedef struct BuildOptions_ bool emit_llvm; bool emit_bitcode; bool test_mode; + bool no_stdlib; } BuildOptions; @@ -235,6 +236,7 @@ typedef struct bool parse_only : 1; bool check_only : 1; bool emit_llvm : 1; + bool no_stdlib : 1; bool emit_object_files : 1; bool no_link : 1; OptimizationLevel optimization_level; diff --git a/src/build/builder.c b/src/build/builder.c index cf7d09e39..690c4e413 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -115,6 +115,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions * { vec_add(target->link_args, options->linker_args[i]); } + target->no_stdlib = options->no_stdlib; target->emit_llvm = options->emit_llvm; switch (options->compile_option) { diff --git a/src/compiler/c_abi_internal.h b/src/compiler/c_abi_internal.h index 198112228..b21f8db61 100644 --- a/src/compiler/c_abi_internal.h +++ b/src/compiler/c_abi_internal.h @@ -21,6 +21,7 @@ ABIArgInfo *abi_arg_new_direct_int_ext(Type *type_to_extend); ABIArgInfo *abi_arg_new_direct_int_ext_by_reg(Type *int_to_extend, bool by_reg); ABIArgInfo *abi_arg_new_direct_coerce_bits(BitSize bits); ABIArgInfo *abi_arg_new_direct_coerce_type(Type *type); +ABIArgInfo *abi_arg_new_direct_coerce_array_type(Type *type, int8_t elements); ABIArgInfo *abi_arg_new_direct_coerce(AbiType type); ABIArgInfo *abi_arg_new_expand_coerce(AbiType target_type, unsigned offset); ABIArgInfo *abi_arg_new_expand_coerce_pair(AbiType first_element, unsigned initial_offset, AbiType second_element, unsigned padding, bool is_packed); @@ -62,7 +63,16 @@ static inline AbiType abi_type_get(Type *type) static inline AbiType abi_type_get_int_bits(BitSize bits) { - return (AbiType) { .int_bits_plus_1 = bits + 1 }; + switch (bits) + { + case 8: + case 16: + case 32: + case 64: + return (AbiType) { .type = type_int_unsigned_by_bitsize(bits) }; + default: + return (AbiType) { .int_bits_plus_1 = bits + 1 }; + } } static inline void abi_type_set_type(AbiType *abi_type, Type *type) diff --git a/src/compiler/codegen_internal.h b/src/compiler/codegen_internal.h index c9d14c6d4..8f35eddca 100644 --- a/src/compiler/codegen_internal.h +++ b/src/compiler/codegen_internal.h @@ -37,3 +37,16 @@ static inline bool abi_info_should_flatten(ABIArgInfo *info) return info->kind == ABI_ARG_DIRECT_COERCE && info->direct_coerce.elements > 1U && !info->direct_coerce.prevent_flatten; } +static inline bool abi_type_is_promotable_integer_or_bool(AbiType type) +{ + if (abi_type_is_type(type)) + { + if (!type_is_integer_or_bool_kind(type.type)) return false; + if (type.type == type_bool) return true; + return type.type->builtin.bitsize < platform_target.width_c_int; + } + // We should only get npot or > big ints here. + assert(!is_power_of_two(type.int_bits_plus_1 - 1) || type.int_bits_plus_1 < platform_target.width_c_int); + return false; +} + diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 0028cb9f8..4c176bc74 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -584,6 +584,7 @@ typedef struct Decl_ bool is_substruct : 1; bool has_variable_array : 1; bool no_scope : 1; + bool escaping : 1; bool is_value : 1; union { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index cb29cc5fd..382f7e46a 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -390,7 +390,6 @@ typedef enum TOKEN_FLOAT128, TOKEN_VARIANT, TOKEN_ANYERR, - TOKEN_FAULT, TOKEN_TYPEID, // Literals. @@ -503,7 +502,7 @@ typedef enum case TOKEN_IPTR: case TOKEN_IPTRDIFF: case TOKEN_ISIZE: case TOKEN_LONG: \ case TOKEN_SHORT: case TOKEN_UINT128: case TOKEN_UINT: case TOKEN_ULONG: \ case TOKEN_UPTR: case TOKEN_UPTRDIFF: case TOKEN_USHORT: case TOKEN_USIZE: \ - case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR: case TOKEN_FAULT: case TOKEN_VARIANT + case TOKEN_FLOAT128: case TOKEN_TYPEID: case TOKEN_ANYERR: case TOKEN_VARIANT #define TYPE_TOKENS NON_VOID_TYPE_TOKENS: case TOKEN_VOID #define TYPELIKE_TOKENS TYPE_TOKENS: case TOKEN_TYPE_IDENT: \ case TOKEN_CT_TYPE_IDENT: case TOKEN_CT_TYPEOF @@ -649,6 +648,7 @@ typedef enum ATTRIBUTE_USED, ATTRIBUTE_NAKED, ATTRIBUTE_NOSCOPE, + ATTRIBUTE_ESCAPING, ATTRIBUTE_CDECL, ATTRIBUTE_STDCALL, ATTRIBUTE_VECCALL, diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index 90670fe93..718c25163 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -96,6 +96,7 @@ ABIArgInfo *abi_arg_new_direct_int_ext(Type *int_to_extend) return abi_arg_new_direct_int_ext_by_reg(int_to_extend, false); } + ABIArgInfo *abi_arg_new_direct_int_ext_by_reg(Type *int_to_extend, bool by_reg) { ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT); @@ -188,6 +189,15 @@ ABIArgInfo *abi_arg_new_direct_coerce_type(Type *type) return info; } +ABIArgInfo *abi_arg_new_direct_coerce_array_type(Type *type, int8_t elements) +{ + assert(elements > 0); + ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT_COERCE); + abi_type_set_type(&info->direct_coerce.type, type); + info->direct_coerce.elements = elements; + return info; +} + ABIArgInfo *abi_arg_new_expand_padded(Type *padding) { ABIArgInfo *info = abi_arg_new(ABI_ARG_EXPAND); diff --git a/src/compiler/llvm_codegen_c_abi_aarch64.c b/src/compiler/llvm_codegen_c_abi_aarch64.c index 1e38fd76f..366f00b29 100644 --- a/src/compiler/llvm_codegen_c_abi_aarch64.c +++ b/src/compiler/llvm_codegen_c_abi_aarch64.c @@ -47,10 +47,8 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type) unsigned members = 0; if (type_is_homogenous_aggregate(type, &base, &members)) { - ABIArgInfo *info = abi_arg_new_direct_coerce_type(base); assert(members < 128); - info->direct_coerce.elements = (uint8_t)members; - return info; + return abi_arg_new_direct_coerce_array_type(base, (int8_t)members); } // Aggregates <= in registers @@ -72,10 +70,9 @@ ABIArgInfo *aarch64_classify_argument_type(Type *type) size = aligned_offset(size, alignment); // We use a pair of i64 for 16-byte aggregate with 8-byte alignment. // For aggregates with 16-byte alignment, we use i128. - ABIArgInfo *info = abi_arg_new_direct_coerce_bits(alignment * 8); + assert(alignment == 8 || alignment == 16); assert(size / alignment < 128); - info->direct_coerce.elements = (uint8_t)(size / alignment); - return info; + return abi_arg_new_direct_coerce_array_type(alignment == 8 ? type_ulong : type_u128, (int8_t)(size / alignment)); } return abi_arg_new_indirect_not_by_val(type); @@ -133,7 +130,7 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic) { return abi_arg_new_direct_coerce_type(type_get_array(type_ulong, size / 8)); } - return abi_arg_new_direct_coerce_bits(aligned_size * 8); + return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(aligned_size * 8)); } return abi_arg_new_indirect_by_val(type); diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index 4a1643355..ff8ebf3de 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -145,6 +145,7 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig assert(type == type->canonical); unsigned xlen = platform_target.riscv.xlen; + assert(is_power_of_two(xlen)); ByteSize size = type_size(type); @@ -223,15 +224,14 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig // required, and a 2-field XLen array if only XLen alignment is required. if (size <= xlen) { - return abi_arg_new_direct_coerce_bits(xlen * 8); + return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(xlen * 8)); } if (alignment == 2 * platform_target.riscv.xlen) { - return abi_arg_new_direct_coerce_bits(xlen * 16); + return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(xlen * 16)); } - ABIArgInfo *info = abi_arg_new_direct_coerce_bits(xlen); - info->direct_coerce.elements = 2; - return info; + Type *ret_type = type_int_unsigned_by_bitsize(xlen * 8); + return abi_arg_new_direct_coerce_array_type(ret_type, 2); } return abi_arg_new_indirect_not_by_val(type); } diff --git a/src/compiler/llvm_codegen_c_abi_win64.c b/src/compiler/llvm_codegen_c_abi_win64.c index 12212a1e9..c28348241 100644 --- a/src/compiler/llvm_codegen_c_abi_win64.c +++ b/src/compiler/llvm_codegen_c_abi_win64.c @@ -63,7 +63,7 @@ ABIArgInfo *win64_classify(Regs *regs, Type *type, bool is_return, bool is_vecto return abi_arg_new_indirect_not_by_val(type); } // Coerce to integer. - return abi_arg_new_direct_coerce_bits(size * 8); + return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(size * 8)); } if (type_is_builtin(type->type_kind)) { diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 2e92b8cc9..5ad0ec2e2 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -550,7 +550,7 @@ AbiType x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_typ case TYPE_U16: case TYPE_U32: case TYPE_I32: - if (!offset) break; + if (offset) break; if (x64_bits_contain_no_user_data(source_type, source_offset + type_size(type), source_offset + 8)) @@ -689,12 +689,9 @@ ABIArgInfo *x64_classify_return(Type *return_type) // AMD64-ABI 3.2.3p4: Rule 3. If the class is INTEGER, the next // available register of the sequence %rax, %rdx is used. result_type = x64_get_int_type_at_offset(return_type, 0, return_type, 0); - if (hi_class == CLASS_NO_CLASS && abi_type_is_integer(result_type)) + if (hi_class == CLASS_NO_CLASS && abi_type_is_promotable_integer_or_bool(result_type)) { - if (type_is_promotable_integer(return_type)) - { - return abi_arg_new_direct_int_ext(return_type); - } + return abi_arg_new_direct_int_ext(return_type); } break; case CLASS_SSE: @@ -779,13 +776,10 @@ static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs case CLASS_INTEGER: needed_registers->int_registers++; result_type = x64_get_int_type_at_offset(type, 0, type, 0); - if (hi_class == CLASS_NO_CLASS && abi_type_is_integer(result_type)) + if (hi_class == CLASS_NO_CLASS && abi_type_is_promotable_integer_or_bool(result_type)) { - // We might need to promote it if it's too small. - if (type_is_promotable_integer(type)) - { - return abi_arg_new_direct_int_ext(type); - } + assert(abi_type_is_type(result_type)); + return abi_arg_new_direct_int_ext(result_type.type); } break; case CLASS_SSE: @@ -904,7 +898,7 @@ void c_abi_func_create_x64(FunctionPrototype *prototype) bool is_regcall = prototype->call_abi == CALL_X86_REG; Registers available_registers = { - .int_registers = is_regcall ? 11 : 16, + .int_registers = is_regcall ? 11 : 6, .sse_registers = is_regcall ? 16 : 8 }; diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index 868f7c41c..e459f7168 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -189,7 +189,7 @@ ABIArgInfo *x86_classify_return(CallABI call, Regs *regs, Type *type) // register, or if it is 64 bits and has a single field. if (size == 1 || size == 2 || size == 4 || (size == 8 && type->vector.len == 1)) { - return abi_arg_new_direct_coerce_bits(size * 8); + return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(size * 8)); } return create_indirect_return_x86(type, regs); } @@ -444,13 +444,13 @@ static inline ABIArgInfo *x86_classify_vector(Regs *regs, Type *type) { if ((size == 1 || size == 2 || size == 4) || (size == 8 && type->vector.len == 1)) { - return abi_arg_new_direct_coerce_bits(size * 8); + return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(size * 8)); } } // MMX passed as i64 if (x86_is_mmxtype(type)) { - return abi_arg_new_direct_coerce_bits(64); + return abi_arg_new_direct_coerce_type(type_ulong); } // Send as a normal parameter @@ -490,9 +490,8 @@ static inline ABIArgInfo *x86_classify_aggregate(CallABI call, Regs *regs, Type // Here we coerce the aggregate into a struct { i32, i32, ... } // but we do not generate this struct immediately here. unsigned size_in_regs = (size + 3) / 4; - ABIArgInfo *info = abi_arg_new_direct_coerce_bits(32); assert(size_in_regs < 8); - info->direct_coerce.elements = (uint8_t)size_in_regs; + ABIArgInfo *info = abi_arg_new_direct_coerce_array_type(type_uint, (int8_t)size_in_regs); // Not in reg on MCU if (!platform_target.x86.is_mcu_api) info->attributes.by_reg = true; return info; diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index ba6130f0e..cf2f0ba74 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -144,22 +144,33 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr } case ABI_ARG_DIRECT_PAIR: { - llvm_emit_and_set_decl_alloca(c, decl); - // Here we do the following transform: - // lo, hi -> { lo, hi } -> struct LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi); LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi); AlignSize decl_alignment = decl->alignment; - // Cast to { lo, hi } - LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair"); + LLVMValueRef coerce; + if (llvm_store_size(c, struct_type) > type_size(decl->type)) + { + AlignSize struct_alignment = llvm_abi_alignment(c, struct_type); + if (decl_alignment < struct_alignment) decl->alignment = decl_alignment = struct_alignment; + coerce = llvm_emit_alloca(c, struct_type, decl_alignment, ""); + decl->backend_ref = LLVMBuildBitCast(c->builder, coerce, llvm_get_ptr_type(c, decl->type), decl->name ? decl->name : "anon"); + } + else + { + llvm_emit_and_set_decl_alloca(c, decl); + // Here we do the following transform: + // lo, hi -> { lo, hi } -> struct + // Cast to { lo, hi } + coerce = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair"); + } // Point to the lo value. AlignSize element_align; - LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 0, decl_alignment, &element_align); + LLVMValueRef lo_ptr = llvm_emit_struct_gep_raw(c, coerce, struct_type, 0, decl_alignment, &element_align); // Store it in the struct. llvm_store(c, lo_ptr, llvm_get_next_param(c, index), element_align); // Point to the hi value. - LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, cast, struct_type, 1, decl_alignment, &element_align); + LLVMValueRef hi_ptr = llvm_emit_struct_gep_raw(c, coerce, struct_type, 1, decl_alignment, &element_align); // Store it in the struct. llvm_store(c, hi_ptr, llvm_get_next_param(c, index), element_align); return; diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 5e762fa07..883601f5c 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -376,16 +376,18 @@ LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info) { unsigned element_index = 0; LLVMTypeRef elements[4]; - // Add padding if needed. + // Add optional padding to make the data appear at the correct offset. if (arg_info->coerce_expand.offset_lo) { - elements[element_index++] = LLVMArrayType(llvm_get_type(c, type_char), arg_info->coerce_expand.offset_lo); + elements[element_index++] = llvm_const_padding_type(c, arg_info->coerce_expand.offset_lo); } elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.lo); + // Add optional padding to make the high field appear at the correct off. if (arg_info->coerce_expand.padding_hi) { elements[element_index++] = LLVMArrayType(llvm_get_type(c, type_char), arg_info->coerce_expand.padding_hi); } + // Check if there is a top type as well. if (abi_type_is_valid(arg_info->coerce_expand.hi)) { elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.hi); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 410006218..5a955f19e 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1579,7 +1579,6 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_VOID] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_TYPEID] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ANYERR] = { parse_type_identifier, NULL, PREC_NONE }, - [TOKEN_FAULT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_VARIANT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 64bd99528..62963f893 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1154,6 +1154,7 @@ AttributeType sema_analyse_attribute(SemaContext *context, Attr *attr, Attribute [ATTRIBUTE_FASTCALL] = ATTR_FUNC, [ATTRIBUTE_OVERLAP] = ATTR_BITSTRUCT, [ATTRIBUTE_NOSCOPE] = ATTR_MACRO, + [ATTRIBUTE_ESCAPING] = ATTR_MACRO, }; if ((attribute_domain[type] & domain) != domain) @@ -1564,6 +1565,10 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) had = decl->no_scope; decl->no_scope = true; break; + case ATTRIBUTE_ESCAPING: + had = decl->escaping; + decl->escaping = true; + break; default: UNREACHABLE } diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 084e6d490..32e96cbd8 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1769,6 +1769,8 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s Ast *body = ast_copy_deep(decl->macro_decl.body); bool no_scope = decl->no_scope; + bool escaping = decl->escaping; + DynamicScope old_scope = context->active_scope; if (!no_scope) { diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 2b2f280f8..79874db13 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -294,7 +294,7 @@ void sema_analysis_run(void) global_context_clear_errors(); - if (global_context.lib_dir) + if (global_context.lib_dir && !active_target.no_stdlib) { file_add_wildcard_files(&global_context.sources, global_context.lib_dir, true, ".c3", ".c3i"); } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index bc21df4d7..39967c565 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -207,6 +207,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_USED] = KW_DEF("used"); attribute_list[ATTRIBUTE_NAKED] = KW_DEF("naked"); attribute_list[ATTRIBUTE_NOSCOPE] = KW_DEF("noscope"); + attribute_list[ATTRIBUTE_ESCAPING] = KW_DEF("escaping"); attribute_list[ATTRIBUTE_CDECL] = KW_DEF("cdecl"); attribute_list[ATTRIBUTE_STDCALL] = KW_DEF("stdcall"); attribute_list[ATTRIBUTE_VECCALL] = KW_DEF("veccall"); diff --git a/src/compiler/tb_codegen.c b/src/compiler/tb_codegen.c index 06ec94365..3af0c0bff 100644 --- a/src/compiler/tb_codegen.c +++ b/src/compiler/tb_codegen.c @@ -270,6 +270,7 @@ static TB_FunctionPrototype *tilde_get_function_type(TB_Module *module, Function break; case ABI_ARG_DIRECT_COERCE: assert(!abi_info_should_flatten(ret_arg_info)); + TODO /* return_type = llvm_get_coerce_type(context, ret_arg_info); diff --git a/src/compiler/tilde_codegen_expr.c b/src/compiler/tilde_codegen_expr.c index 600e645f1..00c64d7b2 100644 --- a/src/compiler/tilde_codegen_expr.c +++ b/src/compiler/tilde_codegen_expr.c @@ -109,7 +109,13 @@ static void tilde_emit_parameter(TbContext *c, TB_Reg **args, ABIArgInfo *info, return; case ABI_ARG_DIRECT_COERCE: { + if (!abi_type_is_type(info->direct_coerce.type)) + { + vec_add(*args, tilde_load_value(c, be_value)); + } TODO + vec_add(*args, tilde_load_value(c, be_value)); + return; /* LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); if (!coerce_type || coerce_type == llvm_get_type(c, type)) diff --git a/src/compiler/tilde_codegen_type.c b/src/compiler/tilde_codegen_type.c new file mode 100644 index 000000000..c6a844e54 --- /dev/null +++ b/src/compiler/tilde_codegen_type.c @@ -0,0 +1,11 @@ +// Copyright (c) 2022 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a LGPLv3.0 +// a copy of which can be found in the LICENSE file. + +#include "tilde_internal.h" + +#if TB_BACKEND + + + +#endif \ No newline at end of file diff --git a/src/compiler/tilde_internal.h b/src/compiler/tilde_internal.h index 2b1790097..e41887561 100644 --- a/src/compiler/tilde_internal.h +++ b/src/compiler/tilde_internal.h @@ -81,6 +81,8 @@ void value_rvalue(TbContext *c, TBEValue *value); TB_Register tilde_get_zero(TbContext *c, Type *type); +// -- type --- + // -- instructions -- void tilde_emit_cond_br(TbContext *c, TBEValue *value, TB_Label then_block, TB_Label else_block); TB_Reg tilde_emit_lshr_fixed(TbContext *c, Type *type, TB_Reg reg, int shift); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index 0c88512e5..8fc3c6213 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -188,8 +188,6 @@ const char *token_type_to_string(TokenType type) return "alias"; case TOKEN_ANYERR: return "anyerr"; - case TOKEN_FAULT: - return "fault"; case TOKEN_AS: return "as"; case TOKEN_ASM: diff --git a/src/compiler/types.c b/src/compiler/types.c index 1431c3a5d..0b09f8fc3 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1295,7 +1295,6 @@ Type *type_from_token(TokenType type) { case TOKEN_VARIANT: return type_any; - case TOKEN_FAULT: case TOKEN_ANYERR: return type_anyerr; case TOKEN_VOID: diff --git a/test/test_suite/abi/darwinx64_1.c3t b/test/test_suite/abi/darwinx64_1.c3t new file mode 100644 index 000000000..42d69ba80 --- /dev/null +++ b/test/test_suite/abi/darwinx64_1.c3t @@ -0,0 +1,48 @@ +// #target: x64-darwin +module test; + +fn char f0() { + return 0; +} + +fn short f1() { + return 0; +} + +fn int f2() { + return 0; +} + +fn float f3() { + return 0; +} + +fn double f4() { + return 0; +} + +fn void f6(char a0, short a1, int a2, long a3, void *a4) {} + +enum Enum7 : int { A, B, C } +fn void f7(Enum7 a0) {} + +struct Struct8 +{ + int a; + int b; +} + +fn Struct8 f8_1() { while (1) {} return Struct8 {}; } +fn void f8_2(Struct8 a0) {} + +/* #expect: test.ll + +define zeroext i8 @test.f0() +define signext i16 @test.f1() +define i32 @test.f2() +define float @test.f3() +define double @test.f4() +define void @test.f6(i8 zeroext %0, i16 signext %1, i32 %2, i64 %3, i8* %4) +define void @test.f7(i32 %0) +define i64 @test.f8_1() +define void @test.f8_2(i64 %0) #0 { diff --git a/test/test_suite/abi/darwinx64_2.c3t b/test/test_suite/abi/darwinx64_2.c3t new file mode 100644 index 000000000..be5684fc1 --- /dev/null +++ b/test/test_suite/abi/darwinx64_2.c3t @@ -0,0 +1,93 @@ +// #target: x64-darwin +module test; + +struct St12 +{ + int a @align(16); +} +fn St12 f12_0(void) { while (1) {}; $unreachable; } +fn void f12_1(St12 a0) {} + +struct St13_0 { long[3] f0; } +struct St13_1 { long[2] f0; } +fn St13_0 f13(int a, int b, int c, int d, + St13_1 e, int f) { while (1) {}; $unreachable; } + +fn void f14(int a, int b, int c, int d, int e, int f, ichar x) {} + +fn void f15(int a, int b, int c, int d, int e, int f, void *x) {} + +fn void f16(float a, float b, float c, float d, float e, float f, float g, float h, + float x) {} + +struct Fl18_s0 { int f0; } +fn void fl18(int a, Fl18_s0 f18_arg1) { while (1) {} } + +struct St20 @align(32) { + int x; + int y; +} +fn void f20(St20 x) {} + +struct StringRef +{ + int x; + char* ptr; +} +fn char *f21(StringRef s) { return s.x+s.ptr; } + +struct St22s @align(16) +{ ulong[2] x; } +fn void f22(St22s x, St22s y) { } + + + +struct St23S { + short f0; + uint f1; + int f2; +} + + +fn void f23(int a, St23S b) { +} + +struct St24s { int a; int b; } + +fn St23S f24(St23S *x, St24s *p2) +{ + return *x; + +} + +fn float[<4>] f25(float[<4>] x) { + return x+x; +} + +/* #expect: test.ll + +define i32 @test.f12_0() +define void @test.f12_1(i32 %0) +define void @test.f13(%St13_0* noalias sret(%St13_0) align 8 %0, i32 %1, i32 %2, i32 %3, i32 %4, %St13_1* byval(%St13_1) align 8 %5, i32 %6) #0 { +define void @test.f14(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i8 signext %6) #0 { +define void @test.f15(i32 %0, i32 %1, i32 %2, i32 %3, i32 %4, i32 %5, i8* %6) + +define void @test.f16(float %0, float %1, float %2, float %3, float %4, float %5, float %6, float %7, float %8) + +define void @test.fl18(i32 %0, i32 %1) +define void @test.f20(%St20* byval(%St20) align 32 %0) +define i8* @test.f21(i64 %0, i8* %1) + +define void @test.f22(i64 %0, i64 %1, i64 %2, i64 %3) +entry: + %x = alloca %St22s, align 16 + %y = alloca %St22s, align 16 + +define void @test.f23(i32 %0, i64 %1, i32 %2) +define { i64, i32 } @test.f24(%St23S* %0, %St24s* %1) + +define <4 x float> @test.f25(<4 x float> %0) #0 { +entry: + %fadd = fadd <4 x float> %0, %0 + ret <4 x float> %fadd +} diff --git a/test/test_suite/abi/small_struct_x64.c3t b/test/test_suite/abi/small_struct_x64.c3t new file mode 100644 index 000000000..d5517f9e1 --- /dev/null +++ b/test/test_suite/abi/small_struct_x64.c3t @@ -0,0 +1,73 @@ +// #target: x64-darwin +module test; +struct Foo +{ + char a; + char b; + char c; +} + +fn int testing() +{ + Foo y = getFoo(Foo { 4, 5, 6 }); + return y.a + y.c; +} + +fn Foo getFoo(Foo f) +{ + return Foo { 1, 2, 3 }; +} + +/* #expect: test.ll + +define i32 @test.testing() #0 { +entry: + %y = alloca %Foo, align 1 + %literal = alloca %Foo, align 1 + %tempcoerce = alloca i24, align 4 + %result = alloca %Foo, align 1 + %0 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 0 + store i8 4, i8* %0, align 1 + %1 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 1 + store i8 5, i8* %1, align 1 + %2 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 2 + store i8 6, i8* %2, align 1 + %3 = bitcast i24* %tempcoerce to i8* + %4 = bitcast %Foo* %literal to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %3, i8* align 1 %4, i32 3, i1 false) + %5 = load i24, i24* %tempcoerce, align 4 + %6 = call i24 @test.getFoo(i24 %5) + %7 = bitcast %Foo* %result to i24* + store i24 %6, i24* %7, align 1 + %8 = bitcast %Foo* %y to i8* + %9 = bitcast %Foo* %result to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %8, i8* align 1 %9, i32 3, i1 false) + %10 = getelementptr inbounds %Foo, %Foo* %y, i32 0, i32 0 + %11 = load i8, i8* %10, align 1 + %uisiext = zext i8 %11 to i32 + %12 = getelementptr inbounds %Foo, %Foo* %y, i32 0, i32 2 + %13 = load i8, i8* %12, align 1 + %uisiext1 = zext i8 %13 to i32 + %add = add i32 %uisiext, %uisiext1 + ret i32 %add +} +; Function Attrs: nounwind +define i24 @test.getFoo(i24 %0) #0 { +entry: + %f = alloca %Foo, align 1 + %literal = alloca %Foo, align 1 + %tempcoerce = alloca i24, align 4 + %1 = bitcast %Foo* %f to i24* + store i24 %0, i24* %1, align 1 + %2 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 0 + store i8 1, i8* %2, align 1 + %3 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 1 + store i8 2, i8* %3, align 1 + %4 = getelementptr inbounds %Foo, %Foo* %literal, i32 0, i32 2 + store i8 3, i8* %4, align 1 + %5 = bitcast i24* %tempcoerce to i8* + %6 = bitcast %Foo* %literal to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %5, i8* align 1 %6, i32 3, i1 false) + %7 = load i24, i24* %tempcoerce, align 4 + ret i24 %7 +} diff --git a/test/test_suite/abi/x64alignarray.c3t b/test/test_suite/abi/x64alignarray.c3t new file mode 100644 index 000000000..e6edf21ca --- /dev/null +++ b/test/test_suite/abi/x64alignarray.c3t @@ -0,0 +1,28 @@ +// #target: x64-darwin +module test; + +extern fn void test1_f(void *); + +fn void test1_g() +{ + float[4] x; + test1_f(&x); +} + +/* #expect: test.ll + +define void @test.test1_g() #0 { +entry: + %x = alloca [4 x float], align 16 + %0 = getelementptr inbounds [4 x float], [4 x float]* %x, i64 0, i64 0 + store float 0.000000e+00, float* %0, align 4 + %1 = getelementptr inbounds [4 x float], [4 x float]* %x, i64 0, i64 1 + store float 0.000000e+00, float* %1, align 4 + %2 = getelementptr inbounds [4 x float], [4 x float]* %x, i64 0, i64 2 + store float 0.000000e+00, float* %2, align 4 + %3 = getelementptr inbounds [4 x float], [4 x float]* %x, i64 0, i64 3 + store float 0.000000e+00, float* %3, align 4 + %ptrptr = bitcast [4 x float]* %x to i8* + call void @test1_f(i8* %ptrptr) + ret void +} \ No newline at end of file diff --git a/test/test_suite/expressions/elvis.c3t b/test/test_suite/expressions/elvis.c3t index 925829c05..99b643af1 100644 --- a/test/test_suite/expressions/elvis.c3t +++ b/test/test_suite/expressions/elvis.c3t @@ -65,3 +65,5 @@ cond.phi: ; preds = %cond.rhs, %entry %4 = zext i1 %val to i8 ret i8 %4 } + +attributes #0 = { nounwind } \ No newline at end of file diff --git a/test/test_suite/expressions/folding_ptr.c3t b/test/test_suite/expressions/folding_ptr.c3t new file mode 100644 index 000000000..1c824ca60 --- /dev/null +++ b/test/test_suite/expressions/folding_ptr.c3t @@ -0,0 +1,34 @@ +// #target: x64-darwin +module test; +struct Test +{ + int x; +} + +extern Test *cfun; + +fn int f() +{ + if (!(cfun + 0)) return 0; + return cfun.x; +} + + +/* #expect: test.ll + +define i32 @test.f() #0 { +entry: + %0 = load %Test*, %Test** @cfun, align 8 + %ptroffset = getelementptr %Test, %Test* %0, i64 0 + %not = icmp eq %Test* %ptroffset, null + br i1 %not, label %if.then, label %if.exit + +if.then: ; preds = %entry + ret i32 0 + +if.exit: ; preds = %entry + %1 = load %Test*, %Test** @cfun, align 8 + %2 = getelementptr inbounds %Test, %Test* %1, i32 0, i32 0 + %3 = load i32, i32* %2, align 8 + ret i32 %3 +}