From 4d5553f545cd9170b3b373a7501a22023a5f0f73 Mon Sep 17 00:00:00 2001
From: Martin Kinkelin <mkinkelin@symmetryinvestments.com>
Date: Sat, 5 Aug 2023 12:21:26 +0200
Subject: [PATCH 1/4] [slight cleanup]

---
 gen/declarations.cpp | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/gen/declarations.cpp b/gen/declarations.cpp
index c0d2ad3ad47..d98652a2ebb 100644
--- a/gen/declarations.cpp
+++ b/gen/declarations.cpp
@@ -419,17 +419,16 @@ class CodegenVisitor : public Visitor {
         std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str();
         gIR->addLinkerOption(llvm::StringRef(arg));
       } else {
-        bool isStaticLib = name.endswith(".a");
+        const bool isStaticLib = name.endswith(".a");
+        const size_t nameLen = name.size();
 
-        size_t const nameLen = name.size();
-        size_t const n = nameLen + 3;
         char *arg = nullptr;
-
-        if (isStaticLib == false) {
+        if (!isStaticLib) { // name => -lname
+          const size_t n = nameLen + 3;
           arg = static_cast<char *>(mem.xmalloc(n));
           arg[0] = '-';
           arg[1] = 'l';
-          memcpy(arg + 2, name.data(), name.size());
+          memcpy(arg + 2, name.data(), nameLen);
           arg[n - 1] = 0;
         } else {
           arg = static_cast<char *>((mem.xmalloc(nameLen + 1)));

From 87b22436e41c6d71fec7c35ff855aa74ca7346d0 Mon Sep 17 00:00:00 2001
From: Martin Kinkelin <mkinkelin@symmetryinvestments.com>
Date: Sat, 5 Aug 2023 12:12:07 +0200
Subject: [PATCH 2/4] Fix function pointers/delegates on Harvard architectures

Resolves issue #4432 by setting the address space of such IR pointers.
---
 gen/tocall.cpp                            |  6 +++---
 ir/irtype.cpp                             |  7 ++++++-
 ir/irtypefunction.cpp                     |  3 ++-
 tests/compilable/funcptr_harvard_gh4432.d | 24 +++++++++++++++++++++++
 4 files changed, 35 insertions(+), 5 deletions(-)
 create mode 100644 tests/compilable/funcptr_harvard_gh4432.d

diff --git a/gen/tocall.cpp b/gen/tocall.cpp
index 47259c3d596..615aa7ba3ea 100644
--- a/gen/tocall.cpp
+++ b/gen/tocall.cpp
@@ -819,7 +819,7 @@ class ImplicitArgumentsBuilder {
 
 ////////////////////////////////////////////////////////////////////////////////
 
-static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) {
+static LLValue *DtoCallableValue(DValue *fn) {
   Type *type = fn->type->toBasetype();
   if (type->ty == TY::Tfunction) {
     return DtoRVal(fn);
@@ -829,7 +829,7 @@ static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) {
       LLValue *dg = DtoLVal(fn);
       llvm::StructType *st = isaStruct(DtoType(fn->type));
       LLValue *funcptr = DtoGEP(st, dg, 0, 1);
-      return DtoLoad(ft->getPointerTo(), funcptr, ".funcptr");
+      return DtoLoad(st->getElementType(1), funcptr, ".funcptr");
     }
     LLValue *dg = DtoRVal(fn);
     assert(isaStruct(dg));
@@ -862,7 +862,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
   }
 
   // get callee llvm value
-  LLValue *callable = DtoCallableValue(irFty.funcType, fnval);
+  LLValue *callable = DtoCallableValue(fnval);
   LLFunctionType *callableTy = irFty.funcType;
   if (dfnval && dfnval->func->isCsymbol()) {
     // See note in DtoDeclareFunction about K&R foward declared (void) functions
diff --git a/ir/irtype.cpp b/ir/irtype.cpp
index 0cf987d9284..d00f35c5386 100644
--- a/ir/irtype.cpp
+++ b/ir/irtype.cpp
@@ -118,10 +118,14 @@ IrTypePointer *IrTypePointer::get(Type *dt) {
   assert(!ctype);
 
   LLType *elemType;
+  unsigned addressSpace = 0;
   if (dt->ty == TY::Tnull) {
     elemType = llvm::Type::getInt8Ty(getGlobalContext());
   } else {
     elemType = DtoMemType(dt->nextOf());
+    if (dt->nextOf()->ty == TY::Tfunction) {
+      addressSpace = gDataLayout->getProgramAddressSpace();
+    }
 
     // DtoType could have already created the same type, e.g. for
     // dt == Node* in struct Node { Node* n; }.
@@ -130,7 +134,8 @@ IrTypePointer *IrTypePointer::get(Type *dt) {
     }
   }
 
-  auto t = new IrTypePointer(dt, llvm::PointerType::get(elemType, 0));
+  auto t =
+      new IrTypePointer(dt, llvm::PointerType::get(elemType, addressSpace));
   ctype = t;
   return t;
 }
diff --git a/ir/irtypefunction.cpp b/ir/irtypefunction.cpp
index 8a1a4ba9a9d..0e830f187af 100644
--- a/ir/irtypefunction.cpp
+++ b/ir/irtypefunction.cpp
@@ -53,7 +53,8 @@ IrTypeDelegate *IrTypeDelegate::get(Type *t) {
   IrFuncTy irFty(tf);
   llvm::Type *ltf =
       DtoFunctionType(tf, irFty, nullptr, Type::tvoid->pointerTo());
-  llvm::Type *types[] = {getVoidPtrType(), getPtrToType(ltf)};
+  llvm::Type *fptr = ltf->getPointerTo(gDataLayout->getProgramAddressSpace());
+  llvm::Type *types[] = {getVoidPtrType(), fptr};
   LLStructType *lt = LLStructType::get(gIR->context(), types, false);
 
   // Could have already built the type as part of a struct forward reference,
diff --git a/tests/compilable/funcptr_harvard_gh4432.d b/tests/compilable/funcptr_harvard_gh4432.d
new file mode 100644
index 00000000000..74d6b9f2c62
--- /dev/null
+++ b/tests/compilable/funcptr_harvard_gh4432.d
@@ -0,0 +1,24 @@
+// A minimal test for function pointers/delegates on a Harvard architecture,
+// with code residing in a separate address space.
+
+// REQUIRES: target_AVR
+// RUN: %ldc -mtriple=avr -betterC -c %s
+
+alias FP = void function();
+alias DG = void delegate();
+
+void foo(FP fp, DG dg)
+{
+    fp();
+    dg();
+}
+
+void bar()
+{
+    foo(() {}, delegate() {});
+
+    FP fp = &bar;
+    DG dg;
+    dg.funcptr = &bar;
+    foo(fp, dg);
+}

From 0d5ea6a2e97c765fb2d33058813e2c4b838c9560 Mon Sep 17 00:00:00 2001
From: Martin Kinkelin <mkinkelin@symmetryinvestments.com>
Date: Sat, 5 Aug 2023 15:53:58 +0200
Subject: [PATCH 3/4] [extend test]

---
 tests/codegen/funcptr_harvard_gh4432.d    | 37 +++++++++++++++++++++++
 tests/compilable/funcptr_harvard_gh4432.d | 24 ---------------
 2 files changed, 37 insertions(+), 24 deletions(-)
 create mode 100644 tests/codegen/funcptr_harvard_gh4432.d
 delete mode 100644 tests/compilable/funcptr_harvard_gh4432.d

diff --git a/tests/codegen/funcptr_harvard_gh4432.d b/tests/codegen/funcptr_harvard_gh4432.d
new file mode 100644
index 00000000000..9400d982b07
--- /dev/null
+++ b/tests/codegen/funcptr_harvard_gh4432.d
@@ -0,0 +1,37 @@
+// Tests function pointers/delegates on a Harvard architecture,
+// with code residing in a separate address space.
+
+// REQUIRES: target_AVR
+// RUN: %ldc -mtriple=avr -betterC -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
+// RUN: %ldc -mtriple=avr -betterC -c %s
+
+alias FP = void function();
+alias DG = void delegate();
+
+// CHECK: @_D22funcptr_harvard_gh44328globalFPPFZv = global {{void \(\) addrspace\(1\)\*|ptr addrspace\(1\)}} @_D22funcptr_harvard_gh44323barFZv, align 2
+__gshared FP globalFP = &bar;
+// CHECK: @_D22funcptr_harvard_gh443217globalDataPointerPPFZv = global {{void \(\) addrspace\(1\)\*\*|ptr}} @_D22funcptr_harvard_gh44328globalFPPFZv, align 2
+__gshared FP* globalDataPointer = &globalFP;
+
+// CHECK: define void @_D22funcptr_harvard_gh44323fooFPFZvDQeZv({{.*}} addrspace(1){{\*?}} %fp_arg, { {{.*}} addrspace(1){{\*?}} } %dg_arg) addrspace(1)
+void foo(FP fp, DG dg)
+{
+    // CHECK: call addrspace(1) void %1()
+    fp();
+    // CHECK: call addrspace(1) void %.funcptr
+    dg();
+    // CHECK-NEXT: ret void
+}
+
+void bar()
+{
+    foo(() {}, delegate() {});
+
+    FP fp = &bar;
+    DG dg;
+    dg.funcptr = &bar;
+    foo(fp, dg);
+
+    dg.funcptr = *globalDataPointer;
+    foo(globalFP, dg);
+}
diff --git a/tests/compilable/funcptr_harvard_gh4432.d b/tests/compilable/funcptr_harvard_gh4432.d
deleted file mode 100644
index 74d6b9f2c62..00000000000
--- a/tests/compilable/funcptr_harvard_gh4432.d
+++ /dev/null
@@ -1,24 +0,0 @@
-// A minimal test for function pointers/delegates on a Harvard architecture,
-// with code residing in a separate address space.
-
-// REQUIRES: target_AVR
-// RUN: %ldc -mtriple=avr -betterC -c %s
-
-alias FP = void function();
-alias DG = void delegate();
-
-void foo(FP fp, DG dg)
-{
-    fp();
-    dg();
-}
-
-void bar()
-{
-    foo(() {}, delegate() {});
-
-    FP fp = &bar;
-    DG dg;
-    dg.funcptr = &bar;
-    foo(fp, dg);
-}

From ab137601df45bb65580fc5ad25b4c1f469f78f32 Mon Sep 17 00:00:00 2001
From: Martin Kinkelin <mkinkelin@symmetryinvestments.com>
Date: Sat, 5 Aug 2023 16:10:16 +0200
Subject: [PATCH 4/4] [add changelog entry]

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index de9002914d1..5a9809c9d74 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 - Supports LLVM 11.0 - 15.0.
 
 #### Bug fixes
+- Fix function pointers/delegates on Harvard architectures (e.g., AVR). (#4432, #4465)
 
 # LDC 1.33.0 (2023-07-23)