diff --git a/src/build_settings.cpp b/src/build_settings.cpp index e6267bb3d..c0f28288f 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -171,6 +171,7 @@ enum Subtarget : u32 { Subtarget_iPhone, Subtarget_iPhoneSimulator, Subtarget_Android, + Subtarget_Playdate, Subtarget_COUNT, Subtarget_Invalid, // NOTE(harold): Must appear after _COUNT as this is not a real subtarget @@ -181,6 +182,7 @@ gb_global String subtarget_strings[Subtarget_COUNT] = { str_lit("iphone"), str_lit("iphonesimulator"), str_lit("android"), + str_lit("playdate"), }; @@ -1957,6 +1959,16 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->no_plt = LLVM_VERSION_MAJOR >= 19; break; } + } else if (subtarget == Subtarget_Playdate) { + // Playdate uses the cortex-m7 as well and an FPU, requiring thumb instructions and hard floats. + bc->microarch = str_lit("cortex-m7"); + bc->metrics.target_triplet = str_lit("thumbv7em-none-eabihf"); + // Remove MOVW/MOVT instructions as playdate only handles R_ARM_ABS32 relocations. + if(bc->target_features_string.len > 0) { + bc->target_features_string = concatenate_strings(permanent_allocator(), bc->target_features_string, str_lit(",no-movt")); + } else { + bc->target_features_string = str_lit("no-movt"); + } } if (metrics->os == TargetOs_windows || diff --git a/src/checker.cpp b/src/checker.cpp index 48863e706..0e1b7d3ca 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1247,6 +1247,7 @@ gb_internal void init_universal(void) { {"iPhone", Subtarget_iPhone}, {"iPhoneSimulator", Subtarget_iPhoneSimulator}, {"Android", Subtarget_Android}, + {"Playdate", Subtarget_Playdate}, }; auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values)); diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 6aabe456f..12c3ff58c 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -186,9 +186,11 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType lbCallingConventionKind cc_kind = lbCallingConvention_C; // TODO(bill): Clean up this logic - if (!is_arch_wasm()) { + if (selected_subtarget == Subtarget_Playdate) { + cc_kind = lbCallingConvention_ARM_AAPCS_VFP; + } else if (!is_arch_wasm()) { cc_kind = lb_calling_convention_map[calling_convention]; - } + } // if (build_context.metrics.arch == TargetArch_amd64) { // if (build_context.metrics.os == TargetOs_windows) { // if (cc_kind == lbCallingConvention_C) { @@ -1554,14 +1556,14 @@ namespace lbAbiWasm { namespace lbAbiArm32 { gb_internal Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention); - gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); + gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention); gb_internal LB_ABI_INFO(abi_info) { LLVMContextRef c = m->ctx; lbFunctionType *ft = permanent_alloc_item(); ft->ctx = c; ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention); - ft->ret = compute_return_type(c, return_type, return_is_defined); + ft->ret = compute_return_type(c, return_type, return_is_defined, calling_convention); ft->calling_convention = calling_convention; return ft; } @@ -1604,7 +1606,10 @@ namespace lbAbiArm32 { } else { i64 sz = lb_sizeof(t); i64 a = lb_alignof(t); - if (is_calling_convention_odin(calling_convention) && sz > 8) { + // Added to support hard floats included in the playdates cortex-m7. + if (calling_convention == ProcCC_CDecl && selected_subtarget == Subtarget_Playdate) { + args[i] = lb_arg_type_direct(t); + } else if (is_calling_convention_odin(calling_convention) && sz > 8) { // Minor change to improve performance using the Odin calling conventions args[i] = lb_arg_type_indirect(t, nullptr); } else if (a <= 4) { @@ -1619,10 +1624,13 @@ namespace lbAbiArm32 { return args; } - gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { + gb_internal lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined, ProcCallingConvention calling_convention) { if (!return_is_defined) { return lb_arg_type_direct(LLVMVoidTypeInContext(c)); } else if (!is_register(return_type, true)) { + if (calling_convention == ProcCC_CDecl && selected_subtarget == Subtarget_Playdate) { + return lb_arg_type_direct(return_type); + } switch (lb_sizeof(return_type)) { case 1: return lb_arg_type_direct(LLVMIntTypeInContext(c, 8), return_type, nullptr, nullptr); case 2: return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index d48f190eb..6aa56c7ed 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -452,7 +452,9 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name Type *pt = p->type; lbCallingConventionKind cc_kind = lbCallingConvention_C; - if (!is_arch_wasm()) { + if (selected_subtarget == Subtarget_Playdate) { + cc_kind = lbCallingConvention_ARM_AAPCS_VFP; + } else if (!is_arch_wasm()) { cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; } LLVMSetFunctionCallConv(p->value, cc_kind); @@ -994,6 +996,9 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue LLVMValueRef ret = LLVMBuildCall2(p->builder, fnp, fn, args, arg_count, ""); auto llvm_cc = lb_calling_convention_map[proc_type->Proc.calling_convention]; + if (selected_subtarget == Subtarget_Playdate) { + llvm_cc = lbCallingConvention_ARM_AAPCS_VFP; + } LLVMSetInstructionCallConv(ret, llvm_cc); LLVMAttributeIndex param_offset = LLVMAttributeIndex_FirstArgIndex; diff --git a/src/main.cpp b/src/main.cpp index da0e6c5b4..44fbcbbb4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1199,8 +1199,10 @@ gb_internal bool parse_build_flags(Array args) { bool found = false; if (selected_target_metrics->metrics->os != TargetOs_darwin && - selected_target_metrics->metrics->os != TargetOs_linux ) { - gb_printf_err("-subtarget can only be used with darwin and linux based targets at the moment\n"); + selected_target_metrics->metrics->os != TargetOs_linux && + (selected_target_metrics->metrics->os != TargetOs_freestanding || + selected_target_metrics->metrics->arch != TargetArch_arm32)) { + gb_printf_err("-subtarget can only be used with darwin, linux or freestanding_arm32 based targets at the moment\n"); bad_flags = true; break; } @@ -3202,7 +3204,7 @@ gb_internal int print_show_help(String const arg0, String command, String option if (build) { if (print_flag("-subtarget:")) { - print_usage_line(2, "[Darwin and Linux only]"); + print_usage_line(2, "[Darwin, Linux and Freestanding ARM32 only]"); print_usage_line(2, "Available subtargets:"); String prefix = str_lit("-subtarget:"); for (u32 i = 1; i < Subtarget_COUNT; i++) {