Skip to content

Commit ed5d95a

Browse files
authored
Merge pull request #588 from scratchcpp/new_compiler
LLVM: Add Compiler class
2 parents 82623b8 + 454c182 commit ed5d95a

File tree

93 files changed

+3442
-24
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+3442
-24
lines changed

.github/workflows/utests-llvm.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Unit tests (LLVM compiler)
2+
3+
on:
4+
push:
5+
branches: '*'
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
env:
10+
BUILD_TYPE: Debug
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v3
18+
with:
19+
submodules: true
20+
21+
- name: Configure CMake
22+
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DLIBSCRATCHCPP_BUILD_UNIT_TESTS=ON -DLIBSCRATCHCPP_USE_LLVM=ON
23+
24+
- name: Build
25+
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j$(nproc --all)
26+
27+
- name: Run unit tests
28+
run: ctest --test-dir build -V

CMakeLists.txt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
99
option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON)
1010
option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON)
1111
option(LIBSCRATCHCPP_COMPUTED_GOTO "Support for computed goto" ON)
12+
option(LIBSCRATCHCPP_USE_LLVM "Compile scripts to LLVM IR (work in progress)" OFF)
13+
option(LIBSCRATCHCPP_PRINT_LLVM_IR "Print LLVM IR of compiled Scratch scripts (for debugging)" OFF)
1214

1315
if (NOT (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
1416
# Computed goto not supported on anything except GCC
@@ -45,7 +47,6 @@ target_sources(scratchcpp
4547
include/scratchcpp/field.h
4648
include/scratchcpp/script.h
4749
include/scratchcpp/broadcast.h
48-
include/scratchcpp/compiler.h
4950
include/scratchcpp/virtualmachine.h
5051
include/scratchcpp/blockprototype.h
5152
include/scratchcpp/block.h
@@ -65,6 +66,25 @@ target_sources(scratchcpp
6566
include/scratchcpp/imonitorhandler.h
6667
)
6768

69+
if (LIBSCRATCHCPP_USE_LLVM)
70+
target_compile_definitions(scratchcpp PUBLIC USE_LLVM)
71+
target_sources(scratchcpp
72+
PUBLIC
73+
include/scratchcpp/dev/compiler.h
74+
include/scratchcpp/dev/executablecode.h
75+
include/scratchcpp/dev/executioncontext.h
76+
)
77+
78+
if(LIBSCRATCHCPP_PRINT_LLVM_IR)
79+
target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR)
80+
endif()
81+
else()
82+
target_sources(scratchcpp
83+
PUBLIC
84+
include/scratchcpp/compiler.h
85+
)
86+
endif()
87+
6888
include(FetchContent)
6989
set(ZIP_SRC thirdparty/zip/src)
7090
set(UTFCPP_SRC thirdparty/utfcpp/source)
@@ -94,6 +114,12 @@ if (LIBSCRATCHCPP_NETWORK_SUPPORT)
94114
target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_NETWORK_SUPPORT)
95115
endif()
96116

117+
if (LIBSCRATCHCPP_USE_LLVM)
118+
include(build/HunterPackages.cmake)
119+
include(build/LLVM.cmake)
120+
target_link_libraries(scratchcpp PRIVATE LLVM)
121+
endif()
122+
97123
target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_LIBRARY)
98124
target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION="${PROJECT_VERSION}")
99125
target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION_MAJOR=${PROJECT_VERSION_MAJOR})

build/HunterPackages.cmake

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# https://layle.me/posts/using-llvm-with-cmake/
2+
# HUNTER_URL is the URL to the latest source code archive on GitHub
3+
# HUNTER_SHA1 is the hash of the downloaded archive
4+
5+
set(OLD_PROJECT_NAME ${PROJECT_NAME})
6+
set(PROJECT_NAME "")
7+
8+
set(HUNTER_URL "https://github.com/scratchcpp/hunter/archive/ee768cdd2c027b5be346f114e726d4b0c4296de6.zip")
9+
set(HUNTER_SHA1 "4A018750743AC656A859C99C655723EAF68EE038")
10+
11+
set(HUNTER_LLVM_VERSION 19.1.0)
12+
set(HUNTER_LLVM_CMAKE_ARGS
13+
LLVM_ENABLE_CRASH_OVERRIDES=OFF
14+
LLVM_ENABLE_ASSERTIONS=ON
15+
LLVM_ENABLE_ZLIB=OFF
16+
LLVM_ENABLE_RTTI=ON
17+
LLVM_BUILD_EXAMPLES=OFF
18+
LLVM_BUILD_TOOLS=OFF
19+
LLVM_BUILD_LLVM_DYLIB=ON
20+
LLVM_INCLUDE_EXAMPLES=OFF
21+
LLVM_TARGETS_TO_BUILD=host
22+
)
23+
24+
set(HUNTER_PACKAGES LLVM)
25+
26+
include(FetchContent)
27+
message(STATUS "Fetching hunter...")
28+
FetchContent_Declare(SetupHunter GIT_REPOSITORY https://github.com/cpp-pm/gate)
29+
FetchContent_MakeAvailable(SetupHunter)
30+
31+
set(PROJECT_NAME ${OLD_PROJECT_NAME})

build/LLVM.cmake

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# https://layle.me/posts/using-llvm-with-cmake/
2+
# This is an INTERFACE target for LLVM, usage:
3+
# target_link_libraries(${PROJECT_NAME} <PRIVATE|PUBLIC|INTERFACE> LLVM)
4+
# The include directories and compile definitions will be properly handled.
5+
6+
set(CMAKE_FOLDER_LLVM "${CMAKE_FOLDER}")
7+
if(CMAKE_FOLDER)
8+
set(CMAKE_FOLDER "${CMAKE_FOLDER}/LLVM")
9+
else()
10+
set(CMAKE_FOLDER "LLVM")
11+
endif()
12+
13+
# Find LLVM
14+
find_package(LLVM REQUIRED CONFIG)
15+
16+
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
17+
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
18+
19+
# Split the definitions properly (https://weliveindetail.github.io/blog/post/2017/07/17/notes-setup.html)
20+
separate_arguments(LLVM_DEFINITIONS)
21+
22+
# Some diagnostics (https://stackoverflow.com/a/17666004/1806760)
23+
message(STATUS "LLVM libraries: ${LLVM_LIBRARIES}")
24+
message(STATUS "LLVM includes: ${LLVM_INCLUDE_DIRS}")
25+
message(STATUS "LLVM definitions: ${LLVM_DEFINITIONS}")
26+
message(STATUS "LLVM tools: ${LLVM_TOOLS_BINARY_DIR}")
27+
28+
if (NOT TARGET LLVM)
29+
add_library(LLVM INTERFACE)
30+
endif()
31+
32+
target_include_directories(LLVM SYSTEM INTERFACE ${LLVM_INCLUDE_DIRS})
33+
target_link_libraries(LLVM INTERFACE ${LLVM_AVAILABLE_LIBS})
34+
target_compile_definitions(LLVM INTERFACE ${LLVM_DEFINITIONS} -DNOMINMAX)
35+
36+
set(CMAKE_FOLDER "${CMAKE_FOLDER_LLVM}")
37+
unset(CMAKE_FOLDER_LLVM)
38+
39+
# MSVC-specific options
40+
if(MSVC)
41+
# This assumes the installed LLVM was built in Release mode
42+
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/ZI /Od /Ob0 /DNDEBUG" CACHE STRING "" FORCE)
43+
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/ZI /Od /Ob0 /DNDEBUG" CACHE STRING "" FORCE)
44+
45+
if(${LLVM_USE_CRT_RELEASE} STREQUAL "MD")
46+
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
47+
elseif(${LLVM_USE_CRT_RELEASE} STREQUAL "MT")
48+
set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
49+
else()
50+
message(FATAL_ERROR "Unsupported LLVM_USE_CRT_RELEASE=${LLVM_USE_CRT_RELEASE}")
51+
endif()
52+
endif()

include/scratchcpp/dev/compiler.h

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <unordered_set>
6+
7+
#include "../global.h"
8+
#include "../spimpl.h"
9+
10+
namespace libscratchcpp
11+
{
12+
13+
class IEngine;
14+
class Target;
15+
class ExecutableCode;
16+
class Variable;
17+
class List;
18+
class Input;
19+
class Field;
20+
class CompilerPrivate;
21+
22+
/*! \brief The Compiler class provides API for compiling Scratch scripts. */
23+
class LIBSCRATCHCPP_EXPORT Compiler
24+
{
25+
public:
26+
Compiler(IEngine *engine, Target *target);
27+
Compiler(const Compiler &) = delete;
28+
29+
IEngine *engine() const;
30+
Target *target() const;
31+
std::shared_ptr<Block> block() const;
32+
33+
std::shared_ptr<ExecutableCode> compile(std::shared_ptr<Block> startBlock);
34+
35+
void addFunctionCall(const std::string &functionName, int argCount, bool returns);
36+
void addConstValue(const Value &value);
37+
void addVariableValue(Variable *variable);
38+
void addListContents(List *list);
39+
void addInput(const std::string &name);
40+
41+
void moveToIf(std::shared_ptr<Block> substack);
42+
void moveToIfElse(std::shared_ptr<Block> substack1, std::shared_ptr<Block> substack2);
43+
void moveToLoop(std::shared_ptr<Block> substack);
44+
void warp();
45+
46+
Field *field(const std::string &name) const;
47+
48+
const std::unordered_set<std::string> &unsupportedBlocks() const;
49+
50+
private:
51+
void addInput(Input *input);
52+
53+
spimpl::unique_impl_ptr<CompilerPrivate> impl;
54+
};
55+
56+
} // namespace libscratchcpp
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include <memory>
6+
7+
#include "../global.h"
8+
9+
namespace libscratchcpp
10+
{
11+
12+
class ExecutionContext;
13+
class Target;
14+
15+
/*! \brief The ExecutableCode class represents the code of a compiled Scratch script. */
16+
class LIBSCRATCHCPP_EXPORT ExecutableCode
17+
{
18+
public:
19+
virtual ~ExecutableCode() { }
20+
21+
/*! Runs the script until it finishes or yields. */
22+
virtual void run(ExecutionContext *context) = 0;
23+
24+
/*! Stops the code. isFinished() will return true. */
25+
virtual void kill(ExecutionContext *context) = 0;
26+
27+
/*! Resets the code to run from the start. */
28+
virtual void reset(ExecutionContext *context) = 0;
29+
30+
/*! Returns true if the code is stopped or finished. */
31+
virtual bool isFinished(ExecutionContext *context) const = 0;
32+
33+
/*! Pauses the script (when it's executed using run() again) until resolvePromise() is called. */
34+
virtual void promise() = 0;
35+
36+
/*! Resolves the promise and resumes the script. */
37+
virtual void resolvePromise() = 0;
38+
39+
/*! Creates an execution context for the given Target. */
40+
virtual std::shared_ptr<ExecutionContext> createExecutionContext(Target *target) const = 0;
41+
};
42+
43+
} // namespace libscratchcpp
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
#pragma once
4+
5+
#include "../global.h"
6+
#include "../spimpl.h"
7+
8+
namespace libscratchcpp
9+
{
10+
11+
class Target;
12+
class ExecutionContextPrivate;
13+
14+
/*! \brief The ExecutionContext represents the execution context of a target (can be a clone) with variables, lists, etc. */
15+
class LIBSCRATCHCPP_EXPORT ExecutionContext
16+
{
17+
public:
18+
ExecutionContext(Target *target);
19+
ExecutionContext(const ExecutionContext &) = delete;
20+
virtual ~ExecutionContext() { }
21+
22+
Target *target() const;
23+
24+
private:
25+
spimpl::unique_impl_ptr<ExecutionContextPrivate> impl;
26+
};
27+
28+
} // namespace libscratchcpp

include/scratchcpp/inputvalue.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ namespace libscratchcpp
1111
{
1212

1313
class Block;
14-
class Compiler;
1514
class Entity;
1615
class InputValuePrivate;
1716

include/scratchcpp/script.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ namespace libscratchcpp
1414
class Target;
1515
class Block;
1616
class IEngine;
17+
#ifdef USE_LLVM
18+
class ExecutableCode;
19+
#endif
1720
class Value;
1821
class Thread;
1922
class Variable;
@@ -34,6 +37,11 @@ class LIBSCRATCHCPP_EXPORT Script
3437
const std::vector<unsigned int> &bytecodeVector() const;
3538
void setBytecode(const std::vector<unsigned int> &code);
3639

40+
#ifdef USE_LLVM
41+
ExecutableCode *code() const;
42+
void setCode(std::shared_ptr<ExecutableCode> code);
43+
#endif
44+
3745
void setHatPredicateBytecode(const std::vector<unsigned int> &code);
3846
bool runHatPredicate(Target *target);
3947

include/scratchcpp/valuedata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ extern "C"
3232
/*! \brief The ValueData struct holds the data of Value. It's used in compiled Scratch code for better performance. */
3333
struct LIBSCRATCHCPP_EXPORT ValueData
3434
{
35+
// NOTE: Any changes must also be done in the LLVM code builder!
3536
union
3637
{
3738
long intValue;

0 commit comments

Comments
 (0)