From 954f988d2b355faa464c5bb7ef05218529fcf2c3 Mon Sep 17 00:00:00 2001 From: MauriceElliott Date: Tue, 23 Jun 2026 22:03:22 +0100 Subject: [PATCH 1/4] feat(build_settings): Added a section to init_build_context to target the thumbv7em-non-eabihf when the cortex-m7 processor is selected --- src/build_settings.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 2a40d0cf5..16b6f2641 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1828,6 +1828,12 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->metrics = *metrics; + if (bc->metrics.os == TargetOs_freestanding && + bc->metrics.arch == TargetArch_arm32 && + bc->microarch == str_lit("cortex-m7")) { + bc->metrics.target_triplet = str_lit("thumbv7em-none-eabihf"); + } + bc->ODIN_OS = target_os_names[metrics->os]; bc->ODIN_ARCH = target_arch_names[metrics->arch]; bc->endian_kind = target_endians[metrics->arch]; From a3fa3476c67293e46d354dd7eea674b9cb552b28 Mon Sep 17 00:00:00 2001 From: MauriceElliott Date: Wed, 24 Jun 2026 21:07:48 +0100 Subject: [PATCH 2/4] feat(arm32 cdecl): Added thumb as the triple for cortex-m7. Updated the calling convention for cortex-m7 arm32 micro processors as the soft float register was being used when calling across the C ABI boundary causing guff data to be passed. --- src/build_settings.cpp | 10 +++++++--- src/llvm_abi.cpp | 20 ++++++++++++++------ src/llvm_backend_proc.cpp | 7 ++++++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 6b943c289..c7c733a34 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1072,6 +1072,12 @@ gb_internal bool is_arch_wasm(void) { return false; } +gb_internal bool is_cortex_m7(void) { + return build_context.metrics.os == TargetOs_freestanding && + build_context.metrics.arch == TargetArch_arm32 && + build_context.microarch == str_lit("cortex-m7"); +} + gb_internal bool is_arch_x86(void) { switch (build_context.metrics.arch) { case TargetArch_i386: @@ -1832,9 +1838,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->metrics = *metrics; - if (bc->metrics.os == TargetOs_freestanding && - bc->metrics.arch == TargetArch_arm32 && - bc->microarch == str_lit("cortex-m7")) { + if (is_cortex_m7()) { bc->metrics.target_triplet = str_lit("thumbv7em-none-eabihf"); } diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 6aabe456f..d4bfe90ce 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 (is_cortex_m7()) { + 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 && is_cortex_m7()) { + 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 && is_cortex_m7()) { + 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..fac697b6f 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 (is_cortex_m7()) { + 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 (is_cortex_m7()) { + llvm_cc = lbCallingConvention_ARM_AAPCS_VFP; + } LLVMSetInstructionCallConv(ret, llvm_cc); LLVMAttributeIndex param_offset = LLVMAttributeIndex_FirstArgIndex; From 56cee0c2cf8d6172180f2069bfdedefc2001247a Mon Sep 17 00:00:00 2001 From: MauriceElliott Date: Wed, 1 Jul 2026 21:18:28 +0100 Subject: [PATCH 3/4] fix: made suggested changes to add a playdate subtarget, updated help text and reduce required build flags for playdate to just the subtarget, no-movt and cortex-m7 added to default on playdate subtarget flag. --- src/build_settings.cpp | 22 +++++++++++++--------- src/checker.cpp | 1 + src/llvm_abi.cpp | 6 +++--- src/llvm_backend_proc.cpp | 4 ++-- src/main.cpp | 8 +++++--- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c7c733a34..e5ee9b2a1 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"), }; @@ -1072,11 +1074,7 @@ gb_internal bool is_arch_wasm(void) { return false; } -gb_internal bool is_cortex_m7(void) { - return build_context.metrics.os == TargetOs_freestanding && - build_context.metrics.arch == TargetArch_arm32 && - build_context.microarch == str_lit("cortex-m7"); -} + gb_internal bool is_arch_x86(void) { switch (build_context.metrics.arch) { @@ -1838,10 +1836,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->metrics = *metrics; - if (is_cortex_m7()) { - bc->metrics.target_triplet = str_lit("thumbv7em-none-eabihf"); - } - bc->ODIN_OS = target_os_names[metrics->os]; bc->ODIN_ARCH = target_arch_names[metrics->arch]; bc->endian_kind = target_endians[metrics->arch]; @@ -1967,6 +1961,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 d4bfe90ce..12c3ff58c 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -186,7 +186,7 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType lbCallingConventionKind cc_kind = lbCallingConvention_C; // TODO(bill): Clean up this logic - if (is_cortex_m7()) { + if (selected_subtarget == Subtarget_Playdate) { cc_kind = lbCallingConvention_ARM_AAPCS_VFP; } else if (!is_arch_wasm()) { cc_kind = lb_calling_convention_map[calling_convention]; @@ -1607,7 +1607,7 @@ namespace lbAbiArm32 { i64 sz = lb_sizeof(t); i64 a = lb_alignof(t); // Added to support hard floats included in the playdates cortex-m7. - if (calling_convention == ProcCC_CDecl && is_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 @@ -1628,7 +1628,7 @@ namespace lbAbiArm32 { if (!return_is_defined) { return lb_arg_type_direct(LLVMVoidTypeInContext(c)); } else if (!is_register(return_type, true)) { - if (calling_convention == ProcCC_CDecl && is_cortex_m7()) { + if (calling_convention == ProcCC_CDecl && selected_subtarget == Subtarget_Playdate) { return lb_arg_type_direct(return_type); } switch (lb_sizeof(return_type)) { diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index fac697b6f..6aa56c7ed 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -452,7 +452,7 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name Type *pt = p->type; lbCallingConventionKind cc_kind = lbCallingConvention_C; - if (is_cortex_m7()) { + 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]; @@ -996,7 +996,7 @@ 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 (is_cortex_m7()) { + if (selected_subtarget == Subtarget_Playdate) { llvm_cc = lbCallingConvention_ARM_AAPCS_VFP; } LLVMSetInstructionCallConv(ret, llvm_cc); 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++) { From adc50a1184c5858ba7d19a6bfbf2bf519e333cd0 Mon Sep 17 00:00:00 2001 From: MauriceElliott Date: Wed, 1 Jul 2026 21:21:02 +0100 Subject: [PATCH 4/4] fixup! fix: made suggested changes to add a playdate subtarget, updated help text and reduce required build flags for playdate to just the subtarget, no-movt and cortex-m7 added to default on playdate subtarget flag. --- src/build_settings.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index e5ee9b2a1..c0f28288f 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1074,8 +1074,6 @@ gb_internal bool is_arch_wasm(void) { return false; } - - gb_internal bool is_arch_x86(void) { switch (build_context.metrics.arch) { case TargetArch_i386: