[clang-repl] Handle mllvm args for clang-repl before calling executeAction (#197133)

Fixes #178139

This work is built on top of discussions done in
https://github.com/llvm/llvm-project/pull/132670
Making a fresh PR cause the branch is a a year old and a bit outdated.

### Problem Statement : This is a 3 part problem statement. Imagine our
compiler builder args look like this
```
  CB.SetCompilerArgs({
      "-v",
      "-xc++",
      "-std=c++23",
      "-Xclang", "-iwithsysroot/include/compat",
      "-fwasm-exceptions",
      "-mllvm", "-wasm-enable-sjlj"
  });
```

**Problem 1** : clang-repl won't respect mllvm args just yet But the
interpreter should be responsible for this. Hence

i) We move relevant code into a function called `parseLLVMArgs` in
`CompilerInstance.cpp`
ii) And we need to add `CI->parseLLVMArgs();` while interpreter creation
in `Interpreter.cpp`

**Result** : We are now at a stage where clang-repl would repsect the
mllvm args passed from the top

**Problem 2** : clang-repl would respect the mllvm but only for the
first `ParseAndExecute` call. So the block parsing the `Runtimes` is
formed correctly. Any cell after that would fail with
```
LLVM ERROR: -exception-model=wasm only allowed with at least one of -wasm-enable-eh or -wasm-enable-sjlj
Aborted()
```

And this is because of this line here.

https://github.com/llvm/llvm-project/blob/3bab75245a2bbbec36206f7e0c569c17a1a487e6/lld/wasm/Driver.cpp#L1315

We call `lldMain` in `Wasm.cpp` which leads us to `linkerMain` in
`lld/wasm/Driver.cpp`.

Now I thought this is a case where wasm-ld is not respecting
"incremental" compilation and resetting options set by the clang
globally. But as Sam's comment conveys
[here](https://github.com/llvm/llvm-project/issues/178139#issuecomment-3806525095)
I think wasm-ld/lld's design **is not wrong**.

My understanding goes like this

1. lld's reset is correct within lld's model. lld was never designed to
coexist with a live incremental compiler in the same process. Its
cleanup code exists for test isolation and is doing exactly what it was
designed to do. Asking lld to skip the reset would break test isolation

2. lld cannot know which options it "owns" vs. which were set by the
caller. LLVM's `cl::` system is a global flat namespace. There is no
ownership or scoping concept. When lld calls
`ResetAllOptionOccurrences()`, it resets *everything* — it literally
cannot distinguish.

3. The options being reset are conceptually the CALLER's responsibility.
`-wasm-enable-eh` and `-wasm-enable-sjlj` are codegen flags set by the
frontend (via `-fwasm-exceptions` / `-mllvm`). The fact that they happen
to live in the same global cl registry that lld resets is an artifact of
LLVM's global option design, not a meaningful coupling between lld and
clang.

The Right Fix: **Restore in WasmIncrementalExecutor::addModule()**

**Result** : clang-repl would respect mllvm across all cells.

<img width="703" height="454" alt="image"
src="https://github.com/user-attachments/assets/862d5459-6e78-41e3-bc03-1bbd8c081edd"
/>

**Problem 3** : Once we've solved the above 2 problems. I added a test
in InterpreterTest.cpp but that woudl fail with
```
[----------] 1 test from InterpreterTest
[ RUN      ] InterpreterTest.EmscriptenExceptionHandling
[DBG CreateCpp] wasm-enable-eh registered: NO
[DBG CreateCpp] wasm-enable-sjlj registered: NO
clang version 23.0.0git (git@github.com:anutosh491/llvm-project.git d1b0c854354cb9bc0033b126fa1f71b2a6c721b2)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir:
 "" -cc1 -triple wasm32-unknown-emscripten -emit-obj -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name "<<< inputs >>>" -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-feature +exception-handling -target-feature +multivalue -target-feature +reference-types -exception-model=wasm -mllvm -wasm-enable-eh -target-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/ -v -fcoverage-compilation-dir=/ -resource-dir lib/clang/23 -internal-isystem lib/clang/23/include -internal-isystem /include/wasm32-emscripten -internal-isystem /include -std=c++23 -fdeprecated-macro -ferror-limit 19 -fmessage-length=80 -fvisibility=default -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -exception-model=wasm -emit-llvm-only -fincremental-extensions -mllvm -wasm-enable-sjlj -o "<<< inputs >>>.o" -x c++ "<<< inputs >>>"
[DBG parseLLVMArgs] wasm-enable-eh registered: NO
[DBG parseLLVMArgs] wasm-enable-sjlj registered: NO
[DBG parseLLVMArgs] args to parse: -wasm-enable-eh -wasm-enable-sjlj
clang (LLVM option parsing): Unknown command line argument '-wasm-enable-eh'.  Try: 'clang (LLVM option parsing) --help'
clang (LLVM option parsing): Did you mean '--rng-seed'?
clang (LLVM option parsing): Unknown command line argument '-wasm-enable-sjlj'.  Try: 'clang (LLVM option parsing) --help'
clang (LLVM option parsing): Did you mean '--sort-timers'?
```

**Root cause** : llvm_shutdown() Destroys the Option Registry

`InterpreterTestBase::TearDownTestSuite()` (in
`InterpreterTestFixture.h`) calls `llvm::llvm_shutdown()` after each
test suite completes. This function destroys all `ManagedStatic<>`
objects, including `ManagedStatic<CommandLineParser>` — the singleton
that holds the entire `cl::opt` registry.

When the next test suite starts and accesses the `CommandLineParser`
again, a fresh empty instance is created. The `cl::opt` global objects
(like`WasmEnableEH`) still exist in memory — their constructors ran at
program
startup — but they have `FullyInitialized = true` and will **not** call
`addArgument()` again. So they remain permanently unregistered in the
new parser.

Fix : Guard llvm_shutdown() on Emscripten. Have added a comment in
`TearDownTestSuite()` for the same.

Now we have 
```
[ RUN      ] InterpreterTest.EmscriptenExceptionHandling
clang version 23.0.0git (git@github.com:anutosh491/llvm-project.git d1b0c854354cb9bc0033b126fa1f71b2a6c721b2)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: 
 "" -cc1 -triple wasm32-unknown-emscripten -emit-obj -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name "<<< inputs >>>" -mrelocation-model static -mframe-pointer=none -ffp-contract=on -fno-rounding-math -mconstructor-aliases -target-feature +exception-handling -target-feature +multivalue -target-feature +reference-types -exception-model=wasm -mllvm -wasm-enable-eh -target-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/ -v -fcoverage-compilation-dir=/ -resource-dir lib/clang/23 -internal-isystem lib/clang/23/include -internal-isystem /include/wasm32-emscripten -internal-isystem /include -std=c++23 -fdeprecated-macro -ferror-limit 19 -fmessage-length=80 -fvisibility=default -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -exception-model=wasm -emit-llvm-only -fincremental-extensions -mllvm -wasm-enable-sjlj -o "<<< inputs >>>.o" -x c++ "<<< inputs >>>"
clang -cc1 version 23.0.0git based upon LLVM 23.0.0git default target wasm32-unknown-emscripten
ignoring nonexistent directory "lib/clang/23/include"
ignoring nonexistent directory "/include/wasm32-emscripten"
ignoring nonexistent directory "/include"
#include "..." search starts here:
End of search list.
[       OK ] InterpreterTest.EmscriptenExceptionHandling (37 ms)
[----------] 10 tests from InterpreterTest (674 ms total)
```
11 files changed
tree: f00d305c26b5129257451c8fdbe33adc26334b34
  1. .ci/
  2. .github/
  3. bolt/
  4. clang/
  5. clang-tools-extra/
  6. cmake/
  7. compiler-rt/
  8. cross-project-tests/
  9. flang/
  10. flang-rt/
  11. libc/
  12. libclc/
  13. libcxx/
  14. libcxxabi/
  15. libsycl/
  16. libunwind/
  17. lld/
  18. lldb/
  19. llvm/
  20. llvm-libgcc/
  21. mlir/
  22. offload/
  23. openmp/
  24. orc-rt/
  25. polly/
  26. runtimes/
  27. third-party/
  28. utils/
  29. .clang-format
  30. .clang-format-ignore
  31. .clang-tidy
  32. .git-blame-ignore-revs
  33. .gitattributes
  34. .gitignore
  35. .mailmap
  36. CODE_OF_CONDUCT.md
  37. CONTRIBUTING.md
  38. LICENSE.TXT
  39. pyproject.toml
  40. README.md
  41. SECURITY.md
README.md

The LLVM Compiler Infrastructure

OpenSSF Scorecard OpenSSF Best Practices libc++

Welcome to the LLVM project!

This repository contains the source code for LLVM, a toolkit for the construction of highly optimized compilers, optimizers, and run-time environments.

The LLVM project has multiple components. The core of the project is itself called “LLVM”. This contains all of the tools, libraries, and header files needed to process intermediate representations and convert them into object files. Tools include an assembler, disassembler, bitcode analyzer, and bitcode optimizer.

C-like languages use the Clang frontend. This component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode -- and from there into object files, using LLVM.

Other components include: the libc++ C++ standard library, the LLD linker, and more.

Getting the Source Code and Building LLVM

Consult the Getting Started with LLVM page for information on building and running LLVM.

For information on how to contribute to the LLVM project, please take a look at the Contributing to LLVM guide.

Getting in touch

Join the LLVM Discourse forums, Discord chat, LLVM Office Hours or Regular sync-ups.

The LLVM project has adopted a code of conduct for participants to all modes of communication within the project.