=======================
LLVM for Chapel release
=======================

This copy of LLVM 19.1.3 is being released with Chapel for
convenience and was obtained from

https://www.llvm.org/

Two Chapel-related changes were made to this copy of the code.
The LLVM test subdirectories were deleted, and the CMakeLists.txt file
was edited not to require the deleted tests.

Any Chapel issues that seem to be related to LLVM should be directed
to the Chapel team at https://chapel-lang.org/bugs.html.

Using LLVM with Chapel
======================

Chapel can be built (by setting CHPL_LLVM=bundled) to include LLVM
in order to enable extern block support and LLVM code generation.

For more information on the current support for LLVM within Chapel,
please refer to $CHPL_HOME/doc/rst/technotes/llvm.rst.  For more
information about LLVM itself, please refer to the website above or to
the README in the llvm-src/ subdirectory of this directory.

Chapel modifications to LLVM
============================

The modifications that we have made to the official LLVM release are
as follows:

* The llvm-src/test and llvm-src/tools/clang/test directories were
  deleted because we do not use them.  Their deletion saves
  significant space and checkout time.

Our deletion of the test directories as noted above makes the
following patch necessary.

* The llvm-src/CMakeLists.txt file was edited to set
  LLVM_INCLUDE_TESTS to OFF by default instead of ON.  It is necessary
  to make this change instead of passing -DLLVM_INCLUDE_TESTS=OFF to
  cmake because Clang's build procedure does not always preserve this
  flag.  With this change made, the appropriate line in
  llvm-src/CMakeLists.txt looks like the following:
option(LLVM_INCLUDE_TESTS "Generate build targets for the LLVM unit tests." OFF)

* The llvm-src/CMakeLists.txt file was also edited to set
  LLVM_INCLUDE_BENCHMARKS to OFF by default instead of ON.
  With this change made, the appropriate line in
  llvm-src/CMakeLists.txt looks like the following:
option(LLVM_INCLUDE_BENCHMARKS "Generate benchmark targets. If OFF, benchmarks can't be built." OFF)

* Patched `lib/Support/APFloat.cpp` to workaround https://github.com/llvm/llvm-project/issues/81013
```
diff --git a/third-party/llvm/llvm-src/lib/Support/APFloat.cpp b/third-party/llvm/llvm-src/lib/Support/APFloat.cpp
index 26b4f8e554..7aa332a787 100644
--- a/third-party/llvm/llvm-src/lib/Support/APFloat.cpp
+++ b/third-party/llvm/llvm-src/lib/Support/APFloat.cpp
@@ -116,9 +116,9 @@ struct fltSemantics {
   /* Number of bits actually used in the semantics. */
   unsigned int sizeInBits;
 
-  fltNonfiniteBehavior nonFiniteBehavior = fltNonfiniteBehavior::IEEE754;
+  fltNonfiniteBehavior nonFiniteBehavior = fltNonfiniteBehavior(int(fltNonfiniteBehavior::IEEE754));
 
-  fltNanEncoding nanEncoding = fltNanEncoding::IEEE;
+  fltNanEncoding nanEncoding = fltNanEncoding(int(fltNanEncoding::IEEE));
   // Returns true if any number described by this semantics can be precisely
   // represented by the specified semantics. Does not take into account
   // the value of fltNonfiniteBehavior.
```
* Patched `lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp` and `lib/Target/AMDGPU/AMDGPUResourceUsageAnalysis.cpp` to workaround https://github.com/llvm/llvm-project/issues/65188
```
diff --git a/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp b/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
index e64e28e01d..3efb9b9b99 100644
--- a/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
+++ b/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
@@ -516,6 +516,7 @@ AMDGPUAsmPrinter::getAmdhsaKernelDescriptor(const MachineFunction &MF,
 }
 
 bool AMDGPUAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
+  if (MF.empty()) return false;
   // Init target streamer lazily on the first function so that previous passes
   // can set metadata.
   if (!IsTargetStreamerInitialized)
diff --git a/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUResourceUsageAnalysis.cpp b/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUResourceUsageAnalysis.cpp
index 146649a7e2..d92d0f1c5c 100644
--- a/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUResourceUsageAnalysis.cpp
+++ b/third-party/llvm/llvm-src/lib/Target/AMDGPU/AMDGPUResourceUsageAnalysis.cpp
@@ -127,15 +127,15 @@ bool AMDGPUResourceUsageAnalysis::runOnModule(Module &M) {
       continue;
 
     MachineFunction *MF = MMI.getMachineFunction(*F);
-    assert(MF && "function must have been generated already");
-
-    auto CI =
-        CallGraphResourceInfo.insert(std::pair(F, SIFunctionResourceInfo()));
-    SIFunctionResourceInfo &Info = CI.first->second;
-    assert(CI.second && "should only be called once per function");
-    Info = analyzeResourceUsage(*MF, TM, AssumedStackSizeForDynamicSizeObjects,
-                                AssumedStackSizeForExternalCall);
-    HasIndirectCall |= Info.HasIndirectCall;
+    if (MF) {
+      auto CI =
+          CallGraphResourceInfo.insert(std::pair(F, SIFunctionResourceInfo()));
+      SIFunctionResourceInfo &Info = CI.first->second;
+      assert(CI.second && "should only be called once per function");
+      Info = analyzeResourceUsage(*MF, TM, AssumedStackSizeForDynamicSizeObjects,
+                                  AssumedStackSizeForExternalCall);
+      HasIndirectCall |= Info.HasIndirectCall;
+    }
   }
 
   // It's possible we have unreachable functions in the module which weren't
@@ -153,10 +153,11 @@ bool AMDGPUResourceUsageAnalysis::runOnModule(Module &M) {
 
     SIFunctionResourceInfo &Info = CI.first->second;
     MachineFunction *MF = MMI.getMachineFunction(*F);
-    assert(MF && "function must have been generated already");
-    Info = analyzeResourceUsage(*MF, TM, AssumedStackSizeForDynamicSizeObjects,
-                                AssumedStackSizeForExternalCall);
-    HasIndirectCall |= Info.HasIndirectCall;
+    if (MF) {
+      Info = analyzeResourceUsage(*MF, TM, AssumedStackSizeForDynamicSizeObjects,
+                                  AssumedStackSizeForExternalCall);
+      HasIndirectCall |= Info.HasIndirectCall;
+    }
   }
 
   if (HasIndirectCall)

```

* Patched `lib/Transforms/Vectorize/SLPVectorizer.cpp` with https://github.com/llvm/llvm-project/commit/709abacdc350d63c61888607edb28ce272daa0a0 to workaround https://github.com/llvm/llvm-project/issues/112577 and https://github.com/llvm/llvm-project/issues/118410
```
diff --git a/third-party/llvm/llvm-src/lib/Transforms/Vectorize/SLPVectorizer.cpp b/third-party/llvm/llvm-src/lib/Transforms/Vectorize/SLPVectorizer.cpp
index ab2b96cdc42..746ba51a981 100644
--- a/third-party/llvm/llvm-src/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/third-party/llvm/llvm-src/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -15440,9 +15440,25 @@ bool BoUpSLP::collectValuesToDemote(
                 MaskedValueIsZero(I->getOperand(1), Mask, SimplifyQuery(*DL)));
       });
     };
+    auto AbsChecker = [&](unsigned BitWidth, unsigned OrigBitWidth) {
+      assert(BitWidth <= OrigBitWidth && "Unexpected bitwidths!");
+      return all_of(E.Scalars, [&](Value *V) {
+        auto *I = cast<Instruction>(V);
+        unsigned SignBits = OrigBitWidth - BitWidth;
+        APInt Mask = APInt::getBitsSetFrom(OrigBitWidth, BitWidth - 1);
+        unsigned Op0SignBits =
+            ComputeNumSignBits(I->getOperand(0), *DL, 0, AC, nullptr, DT);
+        return SignBits <= Op0SignBits &&
+               ((SignBits != Op0SignBits &&
+                 !isKnownNonNegative(I->getOperand(0), SimplifyQuery(*DL))) ||
+                MaskedValueIsZero(I->getOperand(0), Mask, SimplifyQuery(*DL)));
+      });
+    };
     if (ID != Intrinsic::abs) {
       Operands.push_back(getOperandEntry(&E, 1));
       CallChecker = CompChecker;
+    } else {
+      CallChecker = AbsChecker;
     }
     InstructionCost BestCost =
         std::numeric_limits<InstructionCost::CostType>::max();

```

Upgrading LLVM versions
=======================

The directory $CHPL_HOME/third-party/llvm/llvm-src/ contains the
un-tarballed LLVM package contents.  Version updates should be done as
follows, assuming CWD is $CHPL_HOME/third-party/llvm/. Note that steps 1-11 can
be done automatically by $CHPL_HOME/third-party/llvm/update-bundled.sh.

1.  download the new release and signature and verify them with e.g.
    gpg --verify llvm-19.1.3.src.tar.xz.sig
2.  git rm -r llvm-src cmake
3.  un-tarball the new LLVM version into the directory it specifies,
    for example llvm-19.1.3.src
4.  un-tarball the new Clang version into the directory it specifies,
    for example clang-19.1.3.src
5.  un-tarball the cmake support tarball into the directory it specifies,
    for example cmake-19.1.3.src
6.  mv clang-19.1.3.src llvm-19.1.3.src/tools/clang
7.  mv llvm-19.1.3.src llvm-src
8.  mv cmake-19.1.3.src cmake
9.  rm -r llvm-src/test llvm-src/tools/clang/test
10. git add --force llvm-src cmake
    ('--force' is needed to ensure git adds all files in the subdirectory)
11. commit these changes
12. Check above for any patches to apply
13. update the util/chplenv/chpl_llvm.py llvm_versions function
    to record that the new version is available.
14. update $CHPL_HOME/doc/rst/usingchapel/prereqs.rst for the new LLVM version
15. update this README file
16. commit the docs and chplenv changes
17. PR, test, merge, etc.

Updating LLVM versions may also have impacts on GPU support.  See the test
$CHPL_HOME/test/gpu/native/llvmMoved.chpl for a description of these
impacts.
