diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2018-09-07 18:34:19 -0400 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2018-09-07 18:51:34 -0400 |
| commit | 85534a26c6ddea6277e79b12b759188689f3da43 (patch) | |
| tree | 5454004b73e9e940174474666eea9b20dcefe557 /src/codegen.cpp | |
| parent | b18af37c578b118a245c911508d73ed23c303ee0 (diff) | |
| download | zig-85534a26c6ddea6277e79b12b759188689f3da43.tar.gz zig-85534a26c6ddea6277e79b12b759188689f3da43.zip | |
stage1: function to classify x86_64 abi types
Diffstat (limited to 'src/codegen.cpp')
| -rw-r--r-- | src/codegen.cpp | 241 |
1 files changed, 150 insertions, 91 deletions
diff --git a/src/codegen.cpp b/src/codegen.cpp index 8acc7e9702..a38cc869dd 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1894,6 +1894,91 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { report_errors_and_exit(g); } +static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { + assert(alignment > 0); + LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); + LLVMSetAlignment(result, alignment); + return result; +} + +enum X64CABIClass { + X64CABIClass_Unknown, + X64CABIClass_MEMORY, + X64CABIClass_INTEGER, + X64CABIClass_SSE, +}; + +static X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { + size_t ty_size = type_size(g, ty); + if (get_codegen_ptr_type(ty) != nullptr) + return X64CABIClass_INTEGER; + switch (ty->id) { + case ZigTypeIdEnum: + case ZigTypeIdInt: + case ZigTypeIdBool: + return X64CABIClass_INTEGER; + case ZigTypeIdFloat: + return X64CABIClass_SSE; + case ZigTypeIdStruct: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.structure.layout != ContainerLayoutExtern) { + // TODO determine whether packed structs have any unaligned fields + return X64CABIClass_Unknown; + } + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + case ZigTypeIdUnion: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.unionation.layout != ContainerLayoutExtern) + return X64CABIClass_MEMORY; + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + default: + return X64CABIClass_Unknown; + } +} + +// NOTE this does not depend on x86_64 static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { size_t ty_size = type_size(g, ty); if (ty_size > g->pointer_size_bytes) @@ -1905,13 +1990,6 @@ static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { get_codegen_ptr_type(ty) != nullptr); } -static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { - assert(alignment > 0); - LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); - LLVMSetAlignment(result, alignment); - return result; -} - static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { // Initialized from the type for some walks, but because of C var args, // initialized based on callsite instructions for that one. @@ -2047,98 +2125,79 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); size_t ty_size = type_size(g, ty); - if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { + if (abi_class == X64CABIClass_MEMORY) { assert(handle_is_ptr(ty)); - - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) { - switch (fn_walk->id) { - case FnWalkIdAttrs: - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: - fn_walk->data.call.gen_param_values->append(val); - break; - case FnWalkIdTypes: { - ZigType *gen_type = get_pointer_to_type(g, ty, true); - fn_walk->data.types.gen_param_types->append(gen_type->type_ref); - fn_walk->data.types.param_di_types->append(gen_type->di_type); - break; - } - case FnWalkIdVars: { - di_arg_index = fn_walk->data.vars.gen_i; - var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); - dest_ty = get_pointer_to_type(g, ty, false); - fn_walk->data.vars.gen_i += 1; - goto var_ok; - } - case FnWalkIdInits: - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; + switch (fn_walk->id) { + case FnWalkIdAttrs: + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + case FnWalkIdTypes: { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + fn_walk->data.types.gen_param_types->append(gen_type->type_ref); + fn_walk->data.types.param_di_types->append(gen_type->di_type); + break; } - return true; - } - } - if (ty->id == ZigTypeIdStruct) { - assert(handle_is_ptr(ty)); - // "If the size of the aggregate exceeds a single eightbyte, each is classified - // separately. Each eightbyte gets initialized to class NO_CLASS." - if (ty_size <= 8) { - bool contains_int = false; - for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { - contains_int = true; - break; + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); + dest_ty = get_pointer_to_type(g, ty, false); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } + case FnWalkIdInits: + if (var->decl_node) { + gen_var_debug_decl(g, var); } + fn_walk->data.inits.gen_i += 1; + break; + } + return true; + } else if (abi_class == X64CABIClass_INTEGER && ty_size <= 8) { + switch (fn_walk->id) { + case FnWalkIdAttrs: + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: { + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); + LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); + fn_walk->data.call.gen_param_values->append(loaded); + break; } - if (contains_int) { - switch (fn_walk->id) { - case FnWalkIdAttrs: - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: { - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); - LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); - fn_walk->data.call.gen_param_values->append(loaded); - break; - } - case FnWalkIdTypes: { - ZigType *gen_type = get_int_type(g, false, ty_size * 8); - fn_walk->data.types.gen_param_types->append(gen_type->type_ref); - fn_walk->data.types.param_di_types->append(gen_type->di_type); - break; - } - case FnWalkIdVars: { - di_arg_index = fn_walk->data.vars.gen_i; - var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); - fn_walk->data.vars.gen_i += 1; - dest_ty = ty; - goto var_ok; - } - case FnWalkIdInits: { - clear_debug_source_node(g); - LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); - gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; - } + case FnWalkIdTypes: { + ZigType *gen_type = get_int_type(g, false, ty_size * 8); + fn_walk->data.types.gen_param_types->append(gen_type->type_ref); + fn_walk->data.types.param_di_types->append(gen_type->di_type); + break; + } + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); + fn_walk->data.vars.gen_i += 1; + dest_ty = ty; + goto var_ok; + } + case FnWalkIdInits: { + clear_debug_source_node(g); + LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); + gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); + if (var->decl_node) { + gen_var_debug_decl(g, var); } - return true; + fn_walk->data.inits.gen_i += 1; + break; } } + return true; } } if (source_node != nullptr) { |
