From ebce2574f42ac9341d2031fed52e42f3e3774df0 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:22:24 +0200 Subject: [PATCH 01/22] LLVMCodeBuilder: Implement add operator --- src/dev/engine/internal/icodebuilder.h | 1 + src/dev/engine/internal/llvmcodebuilder.cpp | 43 ++++++++++++++++++++ src/dev/engine/internal/llvmcodebuilder.h | 5 +++ test/dev/llvm/llvmcodebuilder_test.cpp | 45 +++++++++++++++++++++ test/mocks/codebuildermock.h | 1 + 5 files changed, 95 insertions(+) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index 5101ae75..4a651a02 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -24,6 +24,7 @@ class ICodeBuilder virtual void addVariableValue(Variable *variable) = 0; virtual void addListContents(List *list) = 0; + virtual void createAdd() = 0; virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 4eaeb3e9..99a3159b 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -88,6 +88,16 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::Add: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0]; + const auto &arg2 = step.args[1]; + llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); + llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); + step.functionReturnReg->value = m_builder.CreateFAdd(num1, num2); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -412,6 +422,11 @@ void LLVMCodeBuilder::addListContents(List *list) { } +void LLVMCodeBuilder::createAdd() +{ + createOp(Step::Type::Add, 2); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); @@ -785,6 +800,34 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type) } } +llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num) +{ + // Replace NaN with zero + llvm::Value *isNaN = m_builder.CreateFCmpUNO(num, num); + return m_builder.CreateSelect(isNaN, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num); +} + +void LLVMCodeBuilder::createOp(Step::Type type, size_t argCount) +{ + Step step(type); + + assert(m_tmpRegs.size() >= argCount); + size_t j = 0; + + for (size_t i = m_tmpRegs.size() - argCount; i < m_tmpRegs.size(); i++) + step.args.push_back({ Compiler::StaticType::Number, m_tmpRegs[i] }); + + m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end()); + + auto ret = std::make_shared(Compiler::StaticType::Number); + ret->isRawValue = true; + step.functionReturnReg = ret; + m_regs[m_currentFunction].push_back(ret); + m_tmpRegs.push_back(ret); + + m_steps.push_back(step); +} + llvm::FunctionCallee LLVMCodeBuilder::resolveFunction(const std::string name, llvm::FunctionType *type) { return m_module->getOrInsertFunction(name, type); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index a0baafa0..80c7ecc6 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -26,6 +26,7 @@ class LLVMCodeBuilder : public ICodeBuilder void addVariableValue(Variable *variable) override; void addListContents(List *list) override; + void createAdd() override; void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -58,6 +59,7 @@ class LLVMCodeBuilder : public ICodeBuilder enum class Type { FunctionCall, + Add, Yield, BeginIf, BeginElse, @@ -124,6 +126,9 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Value *castRawValue(std::shared_ptr reg, Compiler::StaticType targetType); llvm::Value *castConstValue(const Value &value, Compiler::StaticType targetType); llvm::Type *getType(Compiler::StaticType type); + llvm::Value *removeNaN(llvm::Value *num); + + void createOp(Step::Type type, size_t argCount); llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type); llvm::FunctionCallee resolve_value_init(); diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 4b095d66..061f611f 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -200,6 +200,51 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, Add) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createAdd(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->addConstValue(v2); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createAdd(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(expectedResult).toString() + '\n'; + expected += str; + expected += str; + }; + + createBuilder(true); + + addOpTest(50, 25, 75); + addOpTest(-500, 25, -475); + addOpTest(-500, -25, -525); + addOpTest("2.54", "6.28", 8.82); + addOpTest(2.54, "-6.28", -3.74); + addOpTest(true, true, 2); + addOpTest("Infinity", "Infinity", std::numeric_limits::infinity()); + addOpTest("Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); + addOpTest("-Infinity", "Infinity", std::numeric_limits::quiet_NaN()); + addOpTest("-Infinity", "-Infinity", -std::numeric_limits::infinity()); + addOpTest(1, "NaN", 1); + addOpTest("NaN", 1, 1); + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index 0000ed25..a766b9f4 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -14,6 +14,7 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, addVariableValue, (Variable *), (override)); MOCK_METHOD(void, addListContents, (List *), (override)); + MOCK_METHOD(void, createAdd, (), (override)); MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From 3260662e5ddde89bb13c793649ee88416ba5b0b1 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:37:36 +0200 Subject: [PATCH 02/22] LLVMCodeBuilder: Implement subtract operator --- src/dev/engine/internal/icodebuilder.h | 1 + src/dev/engine/internal/llvmcodebuilder.cpp | 15 +++++++ src/dev/engine/internal/llvmcodebuilder.h | 2 + test/dev/llvm/llvmcodebuilder_test.cpp | 45 +++++++++++++++++++++ test/mocks/codebuildermock.h | 1 + 5 files changed, 64 insertions(+) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index 4a651a02..1835df00 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -25,6 +25,7 @@ class ICodeBuilder virtual void addListContents(List *list) = 0; virtual void createAdd() = 0; + virtual void createSub() = 0; virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 99a3159b..a0fe572c 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -98,6 +98,16 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::Sub: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0]; + const auto &arg2 = step.args[1]; + llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); + llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); + step.functionReturnReg->value = m_builder.CreateFSub(num1, num2); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -427,6 +437,11 @@ void LLVMCodeBuilder::createAdd() createOp(Step::Type::Add, 2); } +void LLVMCodeBuilder::createSub() +{ + createOp(Step::Type::Sub, 2); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 80c7ecc6..a7422800 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -27,6 +27,7 @@ class LLVMCodeBuilder : public ICodeBuilder void addListContents(List *list) override; void createAdd() override; + void createSub() override; void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -60,6 +61,7 @@ class LLVMCodeBuilder : public ICodeBuilder { FunctionCall, Add, + Sub, Yield, BeginIf, BeginElse, diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 061f611f..822e15a4 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -245,6 +245,51 @@ TEST_F(LLVMCodeBuilderTest, Add) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, Subtract) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createSub(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->addConstValue(v2); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createSub(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(expectedResult).toString() + '\n'; + expected += str; + expected += str; + }; + + createBuilder(true); + + addOpTest(50, 25, 25); + addOpTest(-500, 25, -525); + addOpTest(-500, -25, -475); + addOpTest("2.54", "6.28", -3.74); + addOpTest(2.54, "-6.28", 8.82); + addOpTest(true, true, 0); + addOpTest("Infinity", "Infinity", std::numeric_limits::quiet_NaN()); + addOpTest("Infinity", "-Infinity", std::numeric_limits::infinity()); + addOpTest("-Infinity", "Infinity", -std::numeric_limits::infinity()); + addOpTest("-Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); + addOpTest(1, "NaN", 1); + addOpTest("NaN", 1, -1); + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index a766b9f4..c2888407 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -15,6 +15,7 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, addListContents, (List *), (override)); MOCK_METHOD(void, createAdd, (), (override)); + MOCK_METHOD(void, createSub, (), (override)); MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From e75947e3fabd2eef6531fc0ed194215999eb0839 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:40:24 +0200 Subject: [PATCH 03/22] LLVMCodeBuilder: Implement multiply operator --- src/dev/engine/internal/icodebuilder.h | 1 + src/dev/engine/internal/llvmcodebuilder.cpp | 15 +++++++ src/dev/engine/internal/llvmcodebuilder.h | 2 + test/dev/llvm/llvmcodebuilder_test.cpp | 50 +++++++++++++++++++++ test/mocks/codebuildermock.h | 1 + 5 files changed, 69 insertions(+) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index 1835df00..e2139b57 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -26,6 +26,7 @@ class ICodeBuilder virtual void createAdd() = 0; virtual void createSub() = 0; + virtual void createMul() = 0; virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index a0fe572c..be0955ca 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -108,6 +108,16 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::Mul: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0]; + const auto &arg2 = step.args[1]; + llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); + llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); + step.functionReturnReg->value = m_builder.CreateFMul(num1, num2); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -442,6 +452,11 @@ void LLVMCodeBuilder::createSub() createOp(Step::Type::Sub, 2); } +void LLVMCodeBuilder::createMul() +{ + createOp(Step::Type::Mul, 2); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index a7422800..fbf782b3 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -28,6 +28,7 @@ class LLVMCodeBuilder : public ICodeBuilder void createAdd() override; void createSub() override; + void createMul() override; void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -62,6 +63,7 @@ class LLVMCodeBuilder : public ICodeBuilder FunctionCall, Add, Sub, + Mul, Yield, BeginIf, BeginElse, diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 822e15a4..d1d3f733 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -290,6 +290,56 @@ TEST_F(LLVMCodeBuilderTest, Subtract) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, Multiply) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createMul(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->addConstValue(v2); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createMul(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(expectedResult).toString() + '\n'; + expected += str; + expected += str; + }; + + createBuilder(true); + + addOpTest(50, 2, 100); + addOpTest(-500, 25, -12500); + addOpTest("-500", -25, 12500); + addOpTest("2.54", "6.28", 15.9512); + addOpTest(true, true, 1); + addOpTest("Infinity", "Infinity", std::numeric_limits::infinity()); + addOpTest("Infinity", 0, std::numeric_limits::quiet_NaN()); + addOpTest("Infinity", 2, std::numeric_limits::infinity()); + addOpTest("Infinity", -2, -std::numeric_limits::infinity()); + addOpTest("Infinity", "-Infinity", -std::numeric_limits::infinity()); + addOpTest("-Infinity", "Infinity", -std::numeric_limits::infinity()); + addOpTest("-Infinity", 0, std::numeric_limits::quiet_NaN()); + addOpTest("-Infinity", 2, -std::numeric_limits::infinity()); + addOpTest("-Infinity", -2, std::numeric_limits::infinity()); + addOpTest("-Infinity", "-Infinity", std::numeric_limits::infinity()); + addOpTest(1, "NaN", 0); + addOpTest("NaN", 1, 0); + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index c2888407..777938bc 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -16,6 +16,7 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createAdd, (), (override)); MOCK_METHOD(void, createSub, (), (override)); + MOCK_METHOD(void, createMul, (), (override)); MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From 31a4e3685cd8d9222c950190e0a887665457b488 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:55:47 +0200 Subject: [PATCH 04/22] Fix conversion of -0.0 to string --- src/scratch/value_functions_p.h | 5 ++++- test/scratch_classes/value_test.cpp | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h index cffe2ab7..1c660731 100644 --- a/src/scratch/value_functions_p.h +++ b/src/scratch/value_functions_p.h @@ -409,7 +409,10 @@ extern "C" inline void value_doubleToString(double v, std::string *dst) { - if (std::isinf(v)) { + if (v == 0) { + dst->assign("0"); + return; + } else if (std::isinf(v)) { if (v > 0) dst->assign("Infinity"); else diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index bb7c71f0..e869a88c 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -1331,6 +1331,18 @@ TEST(ValueTest, ToString) ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); ASSERT_EQ(std::string(cStrings.back()), v.toString()); + v = 0.0; + cStrings.push_back(value_toCString(&v.data())); + ASSERT_EQ(v.toString(), "0"); + ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); + ASSERT_EQ(std::string(cStrings.back()), v.toString()); + + v = -0.0; + cStrings.push_back(value_toCString(&v.data())); + ASSERT_EQ(v.toString(), "0"); + ASSERT_EQ(utf8::utf16to8(v.toUtf16()), v.toString()); + ASSERT_EQ(std::string(cStrings.back()), v.toString()); + v = 2.0; cStrings.push_back(value_toCString(&v.data())); ASSERT_EQ(v.toString(), "2"); @@ -2658,6 +2670,14 @@ TEST(ValueTest, EqualityOperators) TEST(ValueTest, DoubleToCString) { char *ret; + ret = value_doubleToCString(0.0); + ASSERT_EQ(strcmp(ret, "0"), 0); + free(ret); + + ret = value_doubleToCString(-0.0); + ASSERT_EQ(strcmp(ret, "0"), 0); + free(ret); + ret = value_doubleToCString(2.0); ASSERT_EQ(strcmp(ret, "2"), 0); free(ret); From 9183e2be8917e983bf5d7cd50cc6efa4a33d7b3d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:56:13 +0200 Subject: [PATCH 05/22] LLVMCodeBuilder: Implement divide operator --- src/dev/engine/internal/icodebuilder.h | 2 + src/dev/engine/internal/llvmcodebuilder.cpp | 15 ++++++ src/dev/engine/internal/llvmcodebuilder.h | 3 ++ test/dev/llvm/llvmcodebuilder_test.cpp | 59 +++++++++++++++++++++ test/mocks/codebuildermock.h | 2 + 5 files changed, 81 insertions(+) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index e2139b57..c3ddd9dd 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -27,6 +27,8 @@ class ICodeBuilder virtual void createAdd() = 0; virtual void createSub() = 0; virtual void createMul() = 0; + virtual void createDiv() = 0; + virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index be0955ca..6a12ad59 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -118,6 +118,16 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::Div: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0]; + const auto &arg2 = step.args[1]; + llvm::Value *num1 = removeNaN(castValue(arg1.second, arg1.first)); + llvm::Value *num2 = removeNaN(castValue(arg2.second, arg2.first)); + step.functionReturnReg->value = m_builder.CreateFDiv(num1, num2); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -457,6 +467,11 @@ void LLVMCodeBuilder::createMul() createOp(Step::Type::Mul, 2); } +void LLVMCodeBuilder::createDiv() +{ + createOp(Step::Type::Div, 2); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index fbf782b3..fb7366cc 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -29,6 +29,8 @@ class LLVMCodeBuilder : public ICodeBuilder void createAdd() override; void createSub() override; void createMul() override; + void createDiv() override; + void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -64,6 +66,7 @@ class LLVMCodeBuilder : public ICodeBuilder Add, Sub, Mul, + Div, Yield, BeginIf, BeginElse, diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index d1d3f733..406b0db0 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -340,6 +340,65 @@ TEST_F(LLVMCodeBuilderTest, Multiply) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, Divide) +{ + std::string expected; + + auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createDiv(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->addConstValue(v2); + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + m_builder->createDiv(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(expectedResult).toString() + '\n'; + expected += str; + expected += str; + }; + + createBuilder(true); + + addOpTest(50, 2, 25); + addOpTest(-500, 25, -20); + addOpTest("-500", -25, 20); + addOpTest("3.5", "2.5", 1.4); + addOpTest(true, true, 1); + addOpTest("Infinity", "Infinity", std::numeric_limits::quiet_NaN()); + addOpTest("Infinity", 0, std::numeric_limits::infinity()); + addOpTest("Infinity", 2, std::numeric_limits::infinity()); + addOpTest("Infinity", -2, -std::numeric_limits::infinity()); + addOpTest("Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); + addOpTest("-Infinity", "Infinity", std::numeric_limits::quiet_NaN()); + addOpTest("-Infinity", 0, -std::numeric_limits::infinity()); + addOpTest("-Infinity", 2, -std::numeric_limits::infinity()); + addOpTest("-Infinity", -2, std::numeric_limits::infinity()); + addOpTest("-Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); + addOpTest(0, "Infinity", 0); + addOpTest(2, "Infinity", 0); + addOpTest(-2, "Infinity", 0); + addOpTest(0, "-Infinity", 0); + addOpTest(2, "-Infinity", 0); + addOpTest(-2, "-Infinity", 0); + addOpTest(1, "NaN", std::numeric_limits::infinity()); + addOpTest("NaN", 1, 0); + addOpTest(5, 0, std::numeric_limits::infinity()); + addOpTest(-5, 0, -std::numeric_limits::infinity()); + addOpTest(0, 0, std::numeric_limits::quiet_NaN()); + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index 777938bc..fa452b5c 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -17,6 +17,8 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createAdd, (), (override)); MOCK_METHOD(void, createSub, (), (override)); MOCK_METHOD(void, createMul, (), (override)); + MOCK_METHOD(void, createDiv, (), (override)); + MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From 9e7d3d0df756a378e7bf9c72064591f97bff1cde Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 13 Oct 2024 20:01:08 +0200 Subject: [PATCH 06/22] Compiler: Add methods for add, sub, mul and div operators --- include/scratchcpp/dev/compiler.h | 5 +++ src/dev/engine/compiler.cpp | 24 +++++++++++++ test/dev/compiler/compiler_test.cpp | 52 +++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h index 5432a520..bb5da1d5 100644 --- a/include/scratchcpp/dev/compiler.h +++ b/include/scratchcpp/dev/compiler.h @@ -47,6 +47,11 @@ class LIBSCRATCHCPP_EXPORT Compiler void addListContents(List *list); void addInput(const std::string &name); + void createAdd(); + void createSub(); + void createMul(); + void createDiv(); + void moveToIf(std::shared_ptr substack); void moveToIfElse(std::shared_ptr substack1, std::shared_ptr substack2); void moveToRepeatLoop(std::shared_ptr substack); diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp index 355fec48..bb999224 100644 --- a/src/dev/engine/compiler.cpp +++ b/src/dev/engine/compiler.cpp @@ -102,6 +102,30 @@ void Compiler::addInput(const std::string &name) addInput(impl->block->inputAt(impl->block->findInput(name)).get()); } +/*! Creates an add instruction from the last 2 values. */ +void Compiler::createAdd() +{ + impl->builder->createAdd(); +} + +/*! Creates a subtract instruction from the last 2 values. */ +void Compiler::createSub() +{ + impl->builder->createSub(); +} + +/*! Creates a multiply instruction from the last 2 values. */ +void Compiler::createMul() +{ + impl->builder->createMul(); +} + +/*! Creates a divide instruction from the last 2 values. */ +void Compiler::createDiv() +{ + impl->builder->createDiv(); +} + /*! Jumps to the given if substack. */ void Compiler::moveToIf(std::shared_ptr substack) { diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp index e96d2cc5..1b2044ac 100644 --- a/test/dev/compiler/compiler_test.cpp +++ b/test/dev/compiler/compiler_test.cpp @@ -219,6 +219,58 @@ TEST_F(CompilerTest, AddInput) compile(compiler, block); } +TEST_F(CompilerTest, CreateAdd) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createAdd); + compiler->createAdd(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateSub) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createSub); + compiler->createSub(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateMul) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createMul); + compiler->createMul(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateDiv) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createDiv); + compiler->createDiv(); + }); + + compile(compiler, block); +} + TEST_F(CompilerTest, MoveToIf) { Compiler compiler(&m_engine, &m_target); From 833565be284c98b8f7c5c444a055b3d9eb598523 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 14 Oct 2024 23:59:03 +0200 Subject: [PATCH 07/22] LLVMConstValue: Return constant in castConstValue() --- src/dev/engine/internal/llvmcodebuilder.cpp | 2 +- src/dev/engine/internal/llvmcodebuilder.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 6a12ad59..92b5b82e 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -806,7 +806,7 @@ llvm::Value *LLVMCodeBuilder::castRawValue(std::shared_ptr reg, Compil } } -llvm::Value *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::StaticType targetType) +llvm::Constant *LLVMCodeBuilder::castConstValue(const Value &value, Compiler::StaticType targetType) { switch (targetType) { case Compiler::StaticType::Number: diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index fb7366cc..02db5c8b 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -131,7 +131,7 @@ class LLVMCodeBuilder : public ICodeBuilder void freeHeap(); llvm::Value *castValue(std::shared_ptr reg, Compiler::StaticType targetType); llvm::Value *castRawValue(std::shared_ptr reg, Compiler::StaticType targetType); - llvm::Value *castConstValue(const Value &value, Compiler::StaticType targetType); + llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType); llvm::Type *getType(Compiler::StaticType type); llvm::Value *removeNaN(llvm::Value *num); From 361fbb0fc4dc2fa106ff264a308087984a1dbd07 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 14 Oct 2024 23:59:37 +0200 Subject: [PATCH 08/22] LLVMCodeBuilder: Fix ValueData definition --- src/dev/engine/internal/llvmcodebuilder.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 92b5b82e..24d98c0a 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -546,19 +546,11 @@ void LLVMCodeBuilder::yield() void LLVMCodeBuilder::initTypes() { // Create the ValueData struct - llvm::Type *doubleType = llvm::Type::getDoubleTy(m_ctx); // double (numberValue) - llvm::Type *boolType = llvm::Type::getInt1Ty(m_ctx); // bool (boolValue) - llvm::Type *stringPtrType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); // char* (stringValue) + llvm::Type *unionType = m_builder.getInt64Ty(); // 64 bits is the largest size - // Create the union type (largest type size should dominate) - llvm::StructType *unionType = llvm::StructType::create(m_ctx, "union"); - unionType->setBody({ doubleType, boolType, stringPtrType }); - - // Create the full struct type llvm::Type *valueType = llvm::Type::getInt32Ty(m_ctx); // Assuming ValueType is a 32-bit enum llvm::Type *sizeType = llvm::Type::getInt64Ty(m_ctx); // size_t - // Combine them into the full struct m_valueDataType = llvm::StructType::create(m_ctx, "ValueData"); m_valueDataType->setBody({ unionType, valueType, sizeType }); } From 0e92dc8d7ba59a11d743d7d0083d6a983a7276a5 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 00:00:12 +0200 Subject: [PATCH 09/22] LLVMCodeBuilder: Make createOp() universal --- src/dev/engine/internal/llvmcodebuilder.cpp | 12 ++++++------ src/dev/engine/internal/llvmcodebuilder.h | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 24d98c0a..386ada09 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -454,22 +454,22 @@ void LLVMCodeBuilder::addListContents(List *list) void LLVMCodeBuilder::createAdd() { - createOp(Step::Type::Add, 2); + createOp(Step::Type::Add, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createSub() { - createOp(Step::Type::Sub, 2); + createOp(Step::Type::Sub, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createMul() { - createOp(Step::Type::Mul, 2); + createOp(Step::Type::Mul, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createDiv() { - createOp(Step::Type::Div, 2); + createOp(Step::Type::Div, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::beginIfStatement() @@ -844,7 +844,7 @@ llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num) return m_builder.CreateSelect(isNaN, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num); } -void LLVMCodeBuilder::createOp(Step::Type type, size_t argCount) +void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, size_t argCount) { Step step(type); @@ -856,7 +856,7 @@ void LLVMCodeBuilder::createOp(Step::Type type, size_t argCount) m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end()); - auto ret = std::make_shared(Compiler::StaticType::Number); + auto ret = std::make_shared(retType); ret->isRawValue = true; step.functionReturnReg = ret; m_regs[m_currentFunction].push_back(ret); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 02db5c8b..7d8c1996 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -135,7 +135,8 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Type *getType(Compiler::StaticType type); llvm::Value *removeNaN(llvm::Value *num); - void createOp(Step::Type type, size_t argCount); + void createOp(Step::Type type, Compiler::StaticType retType, size_t argCount); + llvm::Value *createValue(std::shared_ptr reg); llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type); llvm::FunctionCallee resolve_value_init(); From 9752ff907b46ff3da9b33136b61009557f3f6c50 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:28:23 +0200 Subject: [PATCH 10/22] LLVMCodeBuilder: Don't print optimized IR --- src/dev/engine/internal/llvmcodebuilder.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 386ada09..e1447f15 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -400,12 +400,6 @@ std::shared_ptr LLVMCodeBuilder::finalize() // Optimize optimize(); -#ifdef PRINT_LLVM_IR - std::cout << std::endl << "=== LLVM IR (" << m_module->getName().str() << ") ===" << std::endl; - m_module->print(llvm::outs(), nullptr); - std::cout << "==============" << std::endl << std::endl; -#endif - return std::make_shared(std::move(m_module)); } From 624392848bda74b72391cbd0352a24330d573053 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:29:59 +0200 Subject: [PATCH 11/22] LLVMCodeBuilder: Make operator tests easier to debug --- test/dev/llvm/llvmcodebuilder_test.cpp | 93 ++++++++++++++------------ 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 406b0db0..1e7f9403 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -9,6 +9,7 @@ using namespace libscratchcpp; using ::testing::Return; +using ::testing::Eq; class LLVMCodeBuilderTest : public testing::Test { @@ -205,6 +206,8 @@ TEST_F(LLVMCodeBuilderTest, Add) std::string expected; auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + createBuilder(true); + m_builder->addConstValue(v1); m_builder->addConstValue(v2); m_builder->createAdd(); @@ -218,11 +221,17 @@ TEST_F(LLVMCodeBuilderTest, Add) m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); std::string str = Value(expectedResult).toString() + '\n'; - expected += str; - expected += str; - }; + std::string expected = str + str; - createBuilder(true); + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; addOpTest(50, 25, 75); addOpTest(-500, 25, -475); @@ -236,13 +245,6 @@ TEST_F(LLVMCodeBuilderTest, Add) addOpTest("-Infinity", "-Infinity", -std::numeric_limits::infinity()); addOpTest(1, "NaN", 1); addOpTest("NaN", 1, 1); - - auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); - - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } TEST_F(LLVMCodeBuilderTest, Subtract) @@ -250,6 +252,8 @@ TEST_F(LLVMCodeBuilderTest, Subtract) std::string expected; auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + createBuilder(true); + m_builder->addConstValue(v1); m_builder->addConstValue(v2); m_builder->createSub(); @@ -263,11 +267,17 @@ TEST_F(LLVMCodeBuilderTest, Subtract) m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); std::string str = Value(expectedResult).toString() + '\n'; - expected += str; - expected += str; - }; + std::string expected = str + str; - createBuilder(true); + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; addOpTest(50, 25, 25); addOpTest(-500, 25, -525); @@ -281,13 +291,6 @@ TEST_F(LLVMCodeBuilderTest, Subtract) addOpTest("-Infinity", "-Infinity", std::numeric_limits::quiet_NaN()); addOpTest(1, "NaN", 1); addOpTest("NaN", 1, -1); - - auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); - - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } TEST_F(LLVMCodeBuilderTest, Multiply) @@ -295,6 +298,8 @@ TEST_F(LLVMCodeBuilderTest, Multiply) std::string expected; auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + createBuilder(true); + m_builder->addConstValue(v1); m_builder->addConstValue(v2); m_builder->createMul(); @@ -308,11 +313,17 @@ TEST_F(LLVMCodeBuilderTest, Multiply) m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); std::string str = Value(expectedResult).toString() + '\n'; - expected += str; - expected += str; - }; + std::string expected = str + str; - createBuilder(true); + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; addOpTest(50, 2, 100); addOpTest(-500, 25, -12500); @@ -331,13 +342,6 @@ TEST_F(LLVMCodeBuilderTest, Multiply) addOpTest("-Infinity", "-Infinity", std::numeric_limits::infinity()); addOpTest(1, "NaN", 0); addOpTest("NaN", 1, 0); - - auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); - - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } TEST_F(LLVMCodeBuilderTest, Divide) @@ -345,6 +349,8 @@ TEST_F(LLVMCodeBuilderTest, Divide) std::string expected; auto addOpTest = [this, &expected](Value v1, Value v2, double expectedResult) { + createBuilder(true); + m_builder->addConstValue(v1); m_builder->addConstValue(v2); m_builder->createDiv(); @@ -358,11 +364,17 @@ TEST_F(LLVMCodeBuilderTest, Divide) m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); std::string str = Value(expectedResult).toString() + '\n'; - expected += str; - expected += str; - }; + std::string expected = str + str; - createBuilder(true); + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; addOpTest(50, 2, 25); addOpTest(-500, 25, -20); @@ -390,13 +402,6 @@ TEST_F(LLVMCodeBuilderTest, Divide) addOpTest(5, 0, std::numeric_limits::infinity()); addOpTest(-5, 0, -std::numeric_limits::infinity()); addOpTest(0, 0, std::numeric_limits::quiet_NaN()); - - auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); - - testing::internal::CaptureStdout(); - code->run(ctx.get()); - ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } TEST_F(LLVMCodeBuilderTest, Yield) From ff362614d4a2534b1f83db7e057abbef55db5508 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:12:46 +0200 Subject: [PATCH 12/22] Compiler: Add Unknown static type --- include/scratchcpp/dev/compiler.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h index bb5da1d5..e98b1605 100644 --- a/include/scratchcpp/dev/compiler.h +++ b/include/scratchcpp/dev/compiler.h @@ -29,7 +29,8 @@ class LIBSCRATCHCPP_EXPORT Compiler Void, Number, Bool, - String + String, + Unknown }; Compiler(IEngine *engine, Target *target); From 29859b51d0bb03809c8e8abbf174c55504bf54fc Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:13:40 +0200 Subject: [PATCH 13/22] LLVMCodeBuilder: Implement equal comparison --- src/dev/engine/internal/icodebuilder.h | 1 + src/dev/engine/internal/llvmcodebuilder.cpp | 223 +++++++++++++++++++- src/dev/engine/internal/llvmcodebuilder.h | 15 ++ test/dev/llvm/llvmcodebuilder_test.cpp | 191 +++++++++++++++++ test/mocks/codebuildermock.h | 1 + 5 files changed, 429 insertions(+), 2 deletions(-) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index c3ddd9dd..6fd80778 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -29,6 +29,7 @@ class ICodeBuilder virtual void createMul() = 0; virtual void createDiv() = 0; + virtual void createCmpEQ() = 0; virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index e1447f15..cc51e404 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -128,6 +128,14 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::CmpEQ: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0].second; + const auto &arg2 = step.args[1].second; + step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::EQ); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -466,6 +474,11 @@ void LLVMCodeBuilder::createDiv() createOp(Step::Type::Div, Compiler::StaticType::Number, 2); } +void LLVMCodeBuilder::createCmpEQ() +{ + createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, 2); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); @@ -831,11 +844,15 @@ llvm::Type *LLVMCodeBuilder::getType(Compiler::StaticType type) } } +llvm::Value *LLVMCodeBuilder::isNaN(llvm::Value *num) +{ + return m_builder.CreateFCmpUNO(num, num); +} + llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num) { // Replace NaN with zero - llvm::Value *isNaN = m_builder.CreateFCmpUNO(num, num); - return m_builder.CreateSelect(isNaN, llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num); + return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num); } void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, size_t argCount) @@ -859,6 +876,184 @@ void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, si m_steps.push_back(step); } +llvm::Value *LLVMCodeBuilder::createValue(std::shared_ptr reg) +{ + if (reg->isConstValue) { + // Create a constant ValueData instance and store it + llvm::Constant *value = castConstValue(reg->constValue, TYPE_MAP[reg->constValue.type()]); + llvm::Value *ret = m_builder.CreateAlloca(m_valueDataType); + + if (reg->constValue.type() == ValueType::String) + value = llvm::ConstantExpr::getPtrToInt(value, m_valueDataType->getElementType(0)); + else + value = llvm::ConstantExpr::getBitCast(value, m_valueDataType->getElementType(0)); + + llvm::Constant *type = m_builder.getInt32(static_cast(reg->constValue.type())); + llvm::Constant *constValue = llvm::ConstantStruct::get(m_valueDataType, { value, type, m_builder.getInt64(0) }); + m_builder.CreateStore(constValue, ret); + + return ret; + } else if (reg->isRawValue) { + llvm::Value *value = castRawValue(reg, reg->type); + llvm::Value *ret = m_builder.CreateAlloca(m_valueDataType); + + // Store value + llvm::Value *valueField = m_builder.CreateStructGEP(m_valueDataType, ret, 0); + m_builder.CreateStore(value, valueField); + + auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [®](const std::pair &pair) { return pair.second == reg->type; }); + + if (it == TYPE_MAP.end()) { + assert(false); + return nullptr; + } + + // Store type + llvm::Value *typeField = m_builder.CreateStructGEP(m_valueDataType, ret, 1); + ValueType type = it->first; + m_builder.CreateStore(m_builder.getInt32(static_cast(type)), typeField); + + return ret; + } else + return reg->value; +} + +llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr arg1, std::shared_ptr arg2, Comparison type) +{ + auto type1 = arg1->type; + auto type2 = arg2->type; + + if (arg1->isConstValue && arg2->isConstValue) { + // If both operands are constant, perform the comparison at compile time + bool result = false; + + switch (type) { + case Comparison::EQ: + result = arg1->constValue == arg2->constValue; + break; + + case Comparison::GT: + result = arg1->constValue > arg2->constValue; + break; + + case Comparison::LT: + result = arg1->constValue < arg2->constValue; + break; + + default: + assert(false); + return nullptr; + } + + return m_builder.getInt1(result); + } else { + // Optimize comparison of constant with number/bool + if (arg1->isConstValue && arg1->constValue.isValidNumber() && (type2 == Compiler::StaticType::Number || type2 == Compiler::StaticType::Bool)) + type1 = Compiler::StaticType::Number; + + if (arg2->isConstValue && arg2->constValue.isValidNumber() && (type1 == Compiler::StaticType::Number || type1 == Compiler::StaticType::Bool)) + type2 = Compiler::StaticType::Number; + + // Optimize number and bool comparison + if (type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::Bool) + type2 = Compiler::StaticType::Number; + + if (type1 == Compiler::StaticType::Bool && type2 == Compiler::StaticType::Number) + type1 = Compiler::StaticType::Number; + + if (type1 != type2 || type1 == Compiler::StaticType::Unknown || type2 == Compiler::StaticType::Unknown) { + // If the types are different or at least one of them + // is unknown, we must use value functions + llvm::Value *value1 = createValue(arg1); + llvm::Value *value2 = createValue(arg2); + + switch (type) { + case Comparison::EQ: + return m_builder.CreateCall(resolve_value_equals(), { value1, value2 }); + + case Comparison::GT: + return m_builder.CreateCall(resolve_value_greater(), { value1, value2 }); + + case Comparison::LT: + return m_builder.CreateCall(resolve_value_lower(), { value1, value2 }); + + default: + assert(false); + return nullptr; + } + } else { + // Compare raw values + llvm::Value *value1 = castValue(arg1, type1); + llvm::Value *value2 = castValue(arg2, type2); + assert(type1 == type2); + + switch (type1) { + case Compiler::StaticType::Number: { + // Compare two numbers + switch (type) { + case Comparison::EQ: { + llvm::Value *nan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN + llvm::Value *cmp = m_builder.CreateFCmpOEQ(value1, value2); + return m_builder.CreateSelect(nan, m_builder.getInt1(true), cmp); + } + + case Comparison::GT: + return m_builder.CreateFCmpOGT(value1, value2); + + case Comparison::LT: + return m_builder.CreateFCmpOLT(value1, value2); + + default: + assert(false); + return nullptr; + } + } + + case Compiler::StaticType::Bool: + // Compare two booleans + switch (type) { + case Comparison::EQ: + return m_builder.CreateICmpEQ(value1, value2); + + case Comparison::GT: + return m_builder.CreateICmpSGT(value1, value2); + + case Comparison::LT: + return m_builder.CreateICmpSLT(value1, value2); + + default: + assert(false); + return nullptr; + } + + case Compiler::StaticType::String: { + // Compare two strings + llvm::Value *cmpRet = m_builder.CreateCall(resolve_strcasecmp(), { value1, value2 }); + + switch (type) { + case Comparison::EQ: + return m_builder.CreateICmpEQ(cmpRet, m_builder.getInt32(0)); + + case Comparison::GT: + return m_builder.CreateICmpSGT(cmpRet, m_builder.getInt32(0)); + + case Comparison::LT: + return m_builder.CreateICmpSLT(cmpRet, m_builder.getInt32(0)); + + default: + assert(false); + return nullptr; + } + } + + default: + assert(false); + return nullptr; + } + } + } +} + llvm::FunctionCallee LLVMCodeBuilder::resolveFunction(const std::string name, llvm::FunctionType *type) { return m_module->getOrInsertFunction(name, type); @@ -933,3 +1128,27 @@ llvm::FunctionCallee LLVMCodeBuilder::resolve_value_stringToBool() { return resolveFunction("value_stringToBool", llvm::FunctionType::get(m_builder.getInt1Ty(), llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0), false)); } + +llvm::FunctionCallee LLVMCodeBuilder::resolve_value_equals() +{ + llvm::Type *valuePtr = m_valueDataType->getPointerTo(); + return resolveFunction("value_equals", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false)); +} + +llvm::FunctionCallee LLVMCodeBuilder::resolve_value_greater() +{ + llvm::Type *valuePtr = m_valueDataType->getPointerTo(); + return resolveFunction("value_greater", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false)); +} + +llvm::FunctionCallee LLVMCodeBuilder::resolve_value_lower() +{ + llvm::Type *valuePtr = m_valueDataType->getPointerTo(); + return resolveFunction("value_lower", llvm::FunctionType::get(m_builder.getInt1Ty(), { valuePtr, valuePtr }, false)); +} + +llvm::FunctionCallee LLVMCodeBuilder::resolve_strcasecmp() +{ + llvm::Type *pointerType = llvm::PointerType::get(llvm::Type::getInt8Ty(m_ctx), 0); + return resolveFunction("strcasecmp", llvm::FunctionType::get(m_builder.getInt32Ty(), { pointerType, pointerType }, false)); +} diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 7d8c1996..6cec0cc6 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -31,6 +31,7 @@ class LLVMCodeBuilder : public ICodeBuilder void createMul() override; void createDiv() override; + void createCmpEQ() override; void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -67,6 +68,7 @@ class LLVMCodeBuilder : public ICodeBuilder Sub, Mul, Div, + CmpEQ, Yield, BeginIf, BeginElse, @@ -122,6 +124,13 @@ class LLVMCodeBuilder : public ICodeBuilder bool warp = false; }; + enum class Comparison + { + EQ, + GT, + LT + }; + void initTypes(); Coroutine initCoroutine(llvm::Function *func); @@ -133,10 +142,12 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Value *castRawValue(std::shared_ptr reg, Compiler::StaticType targetType); llvm::Constant *castConstValue(const Value &value, Compiler::StaticType targetType); llvm::Type *getType(Compiler::StaticType type); + llvm::Value *isNaN(llvm::Value *num); llvm::Value *removeNaN(llvm::Value *num); void createOp(Step::Type type, Compiler::StaticType retType, size_t argCount); llvm::Value *createValue(std::shared_ptr reg); + llvm::Value *createComparison(std::shared_ptr arg1, std::shared_ptr arg2, Comparison type); llvm::FunctionCallee resolveFunction(const std::string name, llvm::FunctionType *type); llvm::FunctionCallee resolve_value_init(); @@ -153,6 +164,10 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::FunctionCallee resolve_value_boolToCString(); llvm::FunctionCallee resolve_value_stringToDouble(); llvm::FunctionCallee resolve_value_stringToBool(); + llvm::FunctionCallee resolve_value_equals(); + llvm::FunctionCallee resolve_value_greater(); + llvm::FunctionCallee resolve_value_lower(); + llvm::FunctionCallee resolve_strcasecmp(); std::string m_id; llvm::LLVMContext m_ctx; diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 1e7f9403..9e444826 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -21,6 +21,27 @@ class LLVMCodeBuilderTest : public testing::Test void createBuilder(bool warp) { m_builder = std::make_unique("test", warp); } + void callConstFuncForType(ValueType type) + { + switch (type) { + case ValueType::Number: + m_builder->addFunctionCall("test_const_number", Compiler::StaticType::Number, { Compiler::StaticType::Number }); + break; + + case ValueType::Bool: + m_builder->addFunctionCall("test_const_bool", Compiler::StaticType::Bool, { Compiler::StaticType::Bool }); + break; + + case ValueType::String: + m_builder->addFunctionCall("test_const_string", Compiler::StaticType::String, { Compiler::StaticType::String }); + break; + + default: + FAIL(); + break; + } + } + std::unique_ptr m_builder; TargetMock m_target; // NOTE: isStage() is used for call expectations }; @@ -404,6 +425,176 @@ TEST_F(LLVMCodeBuilderTest, Divide) addOpTest(0, 0, std::numeric_limits::quiet_NaN()); } +TEST_F(LLVMCodeBuilderTest, EqualComparison) +{ + auto addOpTest = [this](Value v1, Value v2, bool expectedResult) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createCmpEQ(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + callConstFuncForType(v1.type()); + m_builder->addConstValue(v2); + callConstFuncForType(v2.type()); + m_builder->createCmpEQ(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(expectedResult).toString() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; + + addOpTest(10, 10, true); + addOpTest(10, 8, false); + addOpTest(8, 10, false); + + addOpTest(-4.25, -4.25, true); + addOpTest(-4.25, 5.312, false); + addOpTest(5.312, -4.25, false); + + addOpTest(true, true, true); + addOpTest(true, false, false); + addOpTest(false, true, false); + + addOpTest(1, true, true); + addOpTest(1, false, false); + + addOpTest("abC def", "abC def", true); + addOpTest("abC def", "abc dEf", true); + addOpTest("abC def", "ghi Jkl", false); + addOpTest("abC def", "hello world", false); + + addOpTest(" ", "", false); + addOpTest(" ", "0", false); + addOpTest(" ", 0, false); + addOpTest(0, " ", false); + addOpTest("", "0", false); + addOpTest("", 0, false); + addOpTest(0, "", false); + addOpTest("0", 0, true); + addOpTest(0, "0", true); + + addOpTest(5.25, "5.25", true); + addOpTest("5.25", 5.25, true); + addOpTest(5.25, " 5.25", true); + addOpTest(" 5.25", 5.25, true); + addOpTest(5.25, "5.25 ", true); + addOpTest("5.25 ", 5.25, true); + addOpTest(5.25, " 5.25 ", true); + addOpTest(" 5.25 ", 5.25, true); + addOpTest(5.25, "5.26", false); + addOpTest("5.26", 5.25, false); + addOpTest("5.25", "5.26", false); + addOpTest(5, "5 ", true); + addOpTest("5 ", 5, true); + addOpTest(0, "1", false); + addOpTest("1", 0, false); + addOpTest(0, "test", false); + addOpTest("test", 0, false); + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(inf, inf, true); + addOpTest(-inf, -inf, true); + addOpTest(nan, nan, true); + addOpTest(inf, -inf, false); + addOpTest(-inf, inf, false); + addOpTest(inf, nan, false); + addOpTest(nan, inf, false); + addOpTest(-inf, nan, false); + addOpTest(nan, -inf, false); + + addOpTest(5, inf, false); + addOpTest(5, -inf, false); + addOpTest(5, nan, false); + addOpTest(0, nan, false); + + addOpTest(true, "true", true); + addOpTest("true", true, true); + addOpTest(false, "false", true); + addOpTest("false", false, true); + addOpTest(false, "true", false); + addOpTest("true", false, false); + addOpTest(true, "false", false); + addOpTest("false", true, false); + addOpTest(true, "TRUE", true); + addOpTest("TRUE", true, true); + addOpTest(false, "FALSE", true); + addOpTest("FALSE", false, true); + + addOpTest(true, "00001", true); + addOpTest("00001", true, true); + addOpTest(true, "00000", false); + addOpTest("00000", true, false); + addOpTest(false, "00000", true); + addOpTest("00000", false, true); + + addOpTest("true", 1, false); + addOpTest(1, "true", false); + addOpTest("true", 0, false); + addOpTest(0, "true", false); + addOpTest("false", 0, false); + addOpTest(0, "false", false); + addOpTest("false", 1, false); + addOpTest(1, "false", false); + + addOpTest("true", "TRUE", true); + addOpTest("true", "FALSE", false); + addOpTest("false", "FALSE", true); + addOpTest("false", "TRUE", false); + + addOpTest(true, inf, false); + addOpTest(true, -inf, false); + addOpTest(true, nan, false); + addOpTest(false, inf, false); + addOpTest(false, -inf, false); + addOpTest(false, nan, false); + + addOpTest("Infinity", inf, true); + addOpTest("Infinity", -inf, false); + addOpTest("Infinity", nan, false); + addOpTest("infinity", inf, true); + addOpTest("infinity", -inf, false); + addOpTest("infinity", nan, false); + addOpTest("-Infinity", inf, false); + addOpTest("-Infinity", -inf, true); + addOpTest("-Infinity", nan, false); + addOpTest("-infinity", inf, false); + addOpTest("-infinity", -inf, true); + addOpTest("-infinity", nan, false); + addOpTest("NaN", inf, false); + addOpTest("NaN", -inf, false); + addOpTest("NaN", nan, true); + addOpTest("nan", inf, false); + addOpTest("nan", -inf, false); + addOpTest("nan", nan, true); + + addOpTest(inf, "abc", false); + addOpTest(inf, " ", false); + addOpTest(inf, "", false); + addOpTest(inf, "0", false); + addOpTest(-inf, "abc", false); + addOpTest(-inf, " ", false); + addOpTest(-inf, "", false); + addOpTest(-inf, "0", false); + addOpTest(nan, "abc", false); + addOpTest(nan, " ", false); + addOpTest(nan, "", false); + addOpTest(nan, "0", false); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index fa452b5c..473ded0d 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -19,6 +19,7 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createMul, (), (override)); MOCK_METHOD(void, createDiv, (), (override)); + MOCK_METHOD(void, createCmpEQ, (), (override)); MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From c847df46e321616370bdbd09ed89f31695ea62f5 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:53:55 +0200 Subject: [PATCH 14/22] Fix GT and LT value comparison to match Scratch --- src/engine/virtualmachine_p.cpp | 10 ++-- src/scratch/value_functions.cpp | 80 +------------------------ src/scratch/value_functions_p.h | 40 +++++++++++++ test/scratch_classes/value_test.cpp | 92 ++++++++++++++++++++++++++++- 4 files changed, 140 insertions(+), 82 deletions(-) diff --git a/src/engine/virtualmachine_p.cpp b/src/engine/virtualmachine_p.cpp index 6addee35..b1768b91 100644 --- a/src/engine/virtualmachine_p.cpp +++ b/src/engine/virtualmachine_p.cpp @@ -537,20 +537,22 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset) OP(ASIN) : { const Value &v = *READ_REG(0, 1); - if (v < -1 || v > 1) + const double num = v.toDouble(); + if (num < -1 || num > 1) REPLACE_RET_VALUE(std::numeric_limits::quiet_NaN(), 1); else - REPLACE_RET_VALUE(std::asin(v.toDouble()) * 180 / pi, 1); + REPLACE_RET_VALUE(std::asin(num) * 180 / pi, 1); DISPATCH(); } OP(ACOS) : { const Value &v = *READ_REG(0, 1); - if (v < -1 || v > 1) + const double num = v.toDouble(); + if (num < -1 || num > 1) REPLACE_RET_VALUE(std::numeric_limits::quiet_NaN(), 1); else - REPLACE_RET_VALUE(std::acos(v.toDouble()) * 180 / pi, 1); + REPLACE_RET_VALUE(std::acos(num) * 180 / pi, 1); DISPATCH(); } diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp index d13264d0..b7b678b7 100644 --- a/src/scratch/value_functions.cpp +++ b/src/scratch/value_functions.cpp @@ -372,93 +372,19 @@ extern "C" /*! Returns true if the given values are equal. */ bool value_equals(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2) { - // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L121-L150 - assert(v1 && v2); - - if (v1->type == ValueType::Number && v2->type == ValueType::Number) { - if (std::isnan(v1->numberValue) && std::isnan(v2->numberValue)) - return true; - - return v1->numberValue == v2->numberValue; - } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) - return v1->boolValue == v2->boolValue; - - bool ok; - double n1 = value_getNumber(v1, &ok); - double n2; - - if (ok) - n2 = value_getNumber(v2, &ok); - - if (!ok) { - // At least one argument can't be converted to a number - // Scratch compares strings as case insensitive - std::u16string s1, s2; - value_toUtf16(v1, &s1); - value_toUtf16(v2, &s2); - return value_u16StringsEqual(s1, s2); - } - - // Handle the special case of Infinity - if ((static_cast(v1->type) < 0) && (static_cast(v2->type) < 0)) { - assert(!value_isNaN(v1)); - assert(!value_isNaN(v2)); - return v1->type == v2->type; - } - - // Compare as numbers - return n1 == n2; + return value_compare(v1, v2) == 0; } /*! Returns true if the first value is greater than the second value. */ bool value_greater(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2) { - assert(v1 && v2); - - if (v1->type == ValueType::Number && v2->type == ValueType::Number) - return v1->numberValue > v2->numberValue; - else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) - return v1->boolValue > v2->boolValue; - - double n1, n2; - - if (v1->type == ValueType::String) - n1 = value_stringToDouble(v1->stringValue); - else - n1 = value_toDouble(v1); - - if (v2->type == ValueType::String) - n2 = value_stringToDouble(v2->stringValue); - else - n2 = value_toDouble(v2); - - return n1 > n2; + return value_compare(v1, v2) > 0; } /*! Returns true if the first value is lower than the second value. */ bool value_lower(const libscratchcpp::ValueData *v1, const libscratchcpp::ValueData *v2) { - assert(v1 && v2); - - if (v1->type == ValueType::Number && v2->type == ValueType::Number) - return v1->numberValue < v2->numberValue; - else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) - return v1->boolValue < v2->boolValue; - - return value_toDouble(v1) < value_toDouble(v2); - double n1, n2; - - if (v1->type == ValueType::String) - n1 = value_stringToDouble(v1->stringValue); - else - n1 = value_toDouble(v1); - - if (v2->type == ValueType::String) - n2 = value_stringToDouble(v2->stringValue); - else - n2 = value_toDouble(v2); - - return n1 < n2; + return value_compare(v1, v2) < 0; } } diff --git a/src/scratch/value_functions_p.h b/src/scratch/value_functions_p.h index 1c660731..ca497e99 100644 --- a/src/scratch/value_functions_p.h +++ b/src/scratch/value_functions_p.h @@ -539,6 +539,46 @@ extern "C" else return false; } + + inline double value_compare(const ValueData *v1, const ValueData *v2) + { + // https://github.com/scratchfoundation/scratch-vm/blob/112989da0e7306eeb405a5c52616e41c2164af24/src/util/cast.js#L121-L150 + assert(v1 && v2); + + if (v1->type == ValueType::Number && v2->type == ValueType::Number && !std::isnan(v1->numberValue) && !std::isnan(v2->numberValue)) { + // Handle the special case of Infinity + if ((value_isInf(v1->numberValue) && value_isInf(v2->numberValue)) || (value_isNegativeInf(v1->numberValue) && value_isNegativeInf(v2->numberValue))) + return 0; + + return v1->numberValue - v2->numberValue; + } else if (v1->type == ValueType::Bool && v2->type == ValueType::Bool) + return v1->boolValue - v2->boolValue; + + bool ok; + double n1 = value_getNumber(v1, &ok); + double n2; + + if (ok) + n2 = value_getNumber(v2, &ok); + + if (!ok) { + // At least one argument can't be converted to a number + // Scratch compares strings as case insensitive + char *s1 = value_toCString(v1); + char *s2 = value_toCString(v2); + int ret = strcasecmp(s1, s2); + free(s1); + free(s2); + return ret; + } + + // Handle the special case of Infinity + if ((value_isInf(n1) && value_isInf(n2)) || (value_isNegativeInf(n1) && value_isNegativeInf(n2))) + return 0; + + // Compare as numbers + return n1 - n2; + } } } // namespace libscratchcpp diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index e869a88c..87d8d382 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -2188,7 +2188,7 @@ TEST(ValueTest, ModOperator) ASSERT_EQ(v % "-Infinity", -150); } -TEST(ValueTest, EqualityOperators) +TEST(ValueTest, ComparisonOperators) { { Value v1 = 10; @@ -2200,9 +2200,13 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_TRUE(v1 > v3); + ASSERT_FALSE(v1 < v3); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_TRUE(v2 > v3); + ASSERT_FALSE(v2 < v3); } { @@ -2215,9 +2219,13 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_FALSE(v2 > v3); + ASSERT_TRUE(v2 < v3); } { @@ -2230,9 +2238,13 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_TRUE(v1 > v3); + ASSERT_FALSE(v1 < v3); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_TRUE(v2 > v3); + ASSERT_FALSE(v2 < v3); } { @@ -2252,12 +2264,18 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v4); ASSERT_TRUE(v1 != v4); + ASSERT_FALSE(v1 > v4); + ASSERT_TRUE(v1 < v4); ASSERT_FALSE(v2 == v4); ASSERT_TRUE(v2 != v4); + ASSERT_FALSE(v2 > v4); + ASSERT_TRUE(v2 < v4); ASSERT_FALSE(v3 == v4); ASSERT_TRUE(v3 != v4); + ASSERT_FALSE(v3 > v4); + ASSERT_TRUE(v3 < v4); } { @@ -2268,18 +2286,28 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v2); ASSERT_TRUE(v1 != v2); + ASSERT_TRUE(v1 > v2); + ASSERT_FALSE(v1 < v2); ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); ASSERT_FALSE(v1 == v4); ASSERT_TRUE(v1 != v4); + ASSERT_FALSE(v1 > v4); + ASSERT_TRUE(v1 < v4); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_FALSE(v2 > v3); + ASSERT_TRUE(v2 < v3); ASSERT_FALSE(v2 == v4); ASSERT_TRUE(v2 != v4); + ASSERT_FALSE(v2 > v4); + ASSERT_TRUE(v2 < v4); ASSERT_TRUE(v3 == v4); ASSERT_FALSE(v3 != v4); @@ -2304,6 +2332,8 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_TRUE(v1 > v3); + ASSERT_FALSE(v1 < v3); ASSERT_FALSE(v1 == v5); ASSERT_TRUE(v1 != v5); @@ -2322,9 +2352,13 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_FALSE(v2 > v3); + ASSERT_TRUE(v2 < v3); } { @@ -2337,9 +2371,13 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_TRUE(v1 > v3); + ASSERT_FALSE(v1 < v3); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_TRUE(v2 > v3); + ASSERT_FALSE(v2 < v3); } { @@ -2370,9 +2408,13 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v7); ASSERT_TRUE(v1 != v7); + ASSERT_FALSE(v1 > v7); + ASSERT_TRUE(v1 < v7); ASSERT_FALSE(v2 == v7); ASSERT_TRUE(v2 != v7); + ASSERT_FALSE(v2 > v7); + ASSERT_TRUE(v2 < v7); ASSERT_FALSE(v3 == v7); ASSERT_TRUE(v3 != v7); @@ -2401,6 +2443,8 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); ASSERT_FALSE(v1 == v4); ASSERT_TRUE(v1 != v4); @@ -2416,9 +2460,13 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_TRUE(v1 > v3); + ASSERT_FALSE(v1 < v3); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_TRUE(v2 > v3); + ASSERT_FALSE(v2 < v3); } { @@ -2430,15 +2478,29 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); ASSERT_FALSE(v1 == v4); ASSERT_TRUE(v1 != v4); + ASSERT_TRUE(v1 > v4); + ASSERT_FALSE(v1 < v4); ASSERT_FALSE(v1 == v5); ASSERT_TRUE(v1 != v5); ASSERT_FALSE(v2 == v5); ASSERT_TRUE(v2 != v5); + ASSERT_FALSE(v2 > v5); + ASSERT_TRUE(v2 < v5); + + // NaN vs Infinity + ASSERT_FALSE(v3 > v5); + ASSERT_TRUE(v2 < v5); + + // NaN vs -Infinity + ASSERT_FALSE(v4 > v5); + ASSERT_TRUE(v2 < v5); } { @@ -2451,15 +2513,23 @@ TEST(ValueTest, EqualityOperators) ASSERT_TRUE(v1 == v3); ASSERT_FALSE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_FALSE(v1 < v3); ASSERT_TRUE(v2 == v4); ASSERT_FALSE(v2 != v4); + ASSERT_FALSE(v2 > v4); + ASSERT_FALSE(v2 < v4); ASSERT_FALSE(v1 == v2); ASSERT_TRUE(v1 != v2); + ASSERT_TRUE(v1 > v2); + ASSERT_FALSE(v1 < v2); ASSERT_FALSE(v2 == v3); ASSERT_TRUE(v2 != v3); + ASSERT_FALSE(v2 > v3); + ASSERT_TRUE(v2 < v3); ASSERT_TRUE(v1 == v5); ASSERT_FALSE(v1 != v5); @@ -2479,6 +2549,8 @@ TEST(ValueTest, EqualityOperators) ASSERT_FALSE(v1 == v4); ASSERT_TRUE(v1 != v4); + ASSERT_TRUE(v1 > v4); + ASSERT_FALSE(v1 < v4); ASSERT_TRUE(v2 == v4); ASSERT_FALSE(v2 != v4); @@ -2559,12 +2631,18 @@ TEST(ValueTest, EqualityOperators) // Infinity ASSERT_TRUE(v1 == v7); ASSERT_FALSE(v1 != v7); + ASSERT_FALSE(v1 > v7); + ASSERT_FALSE(v1 < v7); ASSERT_FALSE(v1 == v8); ASSERT_TRUE(v1 != v8); + ASSERT_TRUE(v1 > v8); + ASSERT_FALSE(v1 < v8); ASSERT_FALSE(v1 == v9); ASSERT_TRUE(v1 != v9); + ASSERT_FALSE(v1 > v7); + ASSERT_FALSE(v1 < v7); // infinity ASSERT_TRUE(v2 == v7); @@ -2615,6 +2693,18 @@ TEST(ValueTest, EqualityOperators) ASSERT_TRUE(v6 == v9); ASSERT_FALSE(v6 != v9); + + // NaN vs Infinity + ASSERT_TRUE(v5 > v7); + ASSERT_FALSE(v5 < v7); + ASSERT_FALSE(v1 > v9); + ASSERT_TRUE(v1 < v9); + + // NaN vs -Infinity + ASSERT_TRUE(v5 > v8); + ASSERT_FALSE(v5 < v8); + ASSERT_FALSE(v3 > v9); + ASSERT_TRUE(v3 < v9); } { From a70d2800d10e55b672631a2ec4e951fe8a38ce42 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:50:47 +0200 Subject: [PATCH 15/22] LLVMCodeBuilder: Remove expected result from comparison test --- test/dev/llvm/llvmcodebuilder_test.cpp | 272 ++++++++++++------------- 1 file changed, 136 insertions(+), 136 deletions(-) diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 9e444826..82bc89d9 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -427,7 +427,7 @@ TEST_F(LLVMCodeBuilderTest, Divide) TEST_F(LLVMCodeBuilderTest, EqualComparison) { - auto addOpTest = [this](Value v1, Value v2, bool expectedResult) { + auto addOpTest = [this](Value v1, Value v2) { createBuilder(true); m_builder->addConstValue(v1); @@ -442,7 +442,7 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison) m_builder->createCmpEQ(); m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); - std::string str = Value(expectedResult).toString() + '\n'; + std::string str = Value(v1 == v2).toString() + '\n'; std::string expected = str + str; auto code = m_builder->finalize(); @@ -455,144 +455,144 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison) ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; }; - addOpTest(10, 10, true); - addOpTest(10, 8, false); - addOpTest(8, 10, false); - - addOpTest(-4.25, -4.25, true); - addOpTest(-4.25, 5.312, false); - addOpTest(5.312, -4.25, false); - - addOpTest(true, true, true); - addOpTest(true, false, false); - addOpTest(false, true, false); - - addOpTest(1, true, true); - addOpTest(1, false, false); - - addOpTest("abC def", "abC def", true); - addOpTest("abC def", "abc dEf", true); - addOpTest("abC def", "ghi Jkl", false); - addOpTest("abC def", "hello world", false); - - addOpTest(" ", "", false); - addOpTest(" ", "0", false); - addOpTest(" ", 0, false); - addOpTest(0, " ", false); - addOpTest("", "0", false); - addOpTest("", 0, false); - addOpTest(0, "", false); - addOpTest("0", 0, true); - addOpTest(0, "0", true); - - addOpTest(5.25, "5.25", true); - addOpTest("5.25", 5.25, true); - addOpTest(5.25, " 5.25", true); - addOpTest(" 5.25", 5.25, true); - addOpTest(5.25, "5.25 ", true); - addOpTest("5.25 ", 5.25, true); - addOpTest(5.25, " 5.25 ", true); - addOpTest(" 5.25 ", 5.25, true); - addOpTest(5.25, "5.26", false); - addOpTest("5.26", 5.25, false); - addOpTest("5.25", "5.26", false); - addOpTest(5, "5 ", true); - addOpTest("5 ", 5, true); - addOpTest(0, "1", false); - addOpTest("1", 0, false); - addOpTest(0, "test", false); - addOpTest("test", 0, false); + addOpTest(10, 10); + addOpTest(10, 8); + addOpTest(8, 10); + + addOpTest(-4.25, -4.25); + addOpTest(-4.25, 5.312); + addOpTest(5.312, -4.25); + + addOpTest(true, true); + addOpTest(true, false); + addOpTest(false, true); + + addOpTest(1, true); + addOpTest(1, false); + + addOpTest("abC def", "abC def"); + addOpTest("abC def", "abc dEf"); + addOpTest("abC def", "ghi Jkl"); + addOpTest("abC def", "hello world"); + + addOpTest(" ", ""); + addOpTest(" ", "0"); + addOpTest(" ", 0); + addOpTest(0, " "); + addOpTest("", "0"); + addOpTest("", 0); + addOpTest(0, ""); + addOpTest("0", 0); + addOpTest(0, "0"); + + addOpTest(5.25, "5.25"); + addOpTest("5.25", 5.25); + addOpTest(5.25, " 5.25"); + addOpTest(" 5.25", 5.25); + addOpTest(5.25, "5.25 "); + addOpTest("5.25 ", 5.25); + addOpTest(5.25, " 5.25 "); + addOpTest(" 5.25 ", 5.25); + addOpTest(5.25, "5.26"); + addOpTest("5.26", 5.25); + addOpTest("5.25", "5.26"); + addOpTest(5, "5 "); + addOpTest("5 ", 5); + addOpTest(0, "1"); + addOpTest("1", 0); + addOpTest(0, "test"); + addOpTest("test", 0); static const double inf = std::numeric_limits::infinity(); static const double nan = std::numeric_limits::quiet_NaN(); - addOpTest(inf, inf, true); - addOpTest(-inf, -inf, true); - addOpTest(nan, nan, true); - addOpTest(inf, -inf, false); - addOpTest(-inf, inf, false); - addOpTest(inf, nan, false); - addOpTest(nan, inf, false); - addOpTest(-inf, nan, false); - addOpTest(nan, -inf, false); - - addOpTest(5, inf, false); - addOpTest(5, -inf, false); - addOpTest(5, nan, false); - addOpTest(0, nan, false); - - addOpTest(true, "true", true); - addOpTest("true", true, true); - addOpTest(false, "false", true); - addOpTest("false", false, true); - addOpTest(false, "true", false); - addOpTest("true", false, false); - addOpTest(true, "false", false); - addOpTest("false", true, false); - addOpTest(true, "TRUE", true); - addOpTest("TRUE", true, true); - addOpTest(false, "FALSE", true); - addOpTest("FALSE", false, true); - - addOpTest(true, "00001", true); - addOpTest("00001", true, true); - addOpTest(true, "00000", false); - addOpTest("00000", true, false); - addOpTest(false, "00000", true); - addOpTest("00000", false, true); - - addOpTest("true", 1, false); - addOpTest(1, "true", false); - addOpTest("true", 0, false); - addOpTest(0, "true", false); - addOpTest("false", 0, false); - addOpTest(0, "false", false); - addOpTest("false", 1, false); - addOpTest(1, "false", false); - - addOpTest("true", "TRUE", true); - addOpTest("true", "FALSE", false); - addOpTest("false", "FALSE", true); - addOpTest("false", "TRUE", false); - - addOpTest(true, inf, false); - addOpTest(true, -inf, false); - addOpTest(true, nan, false); - addOpTest(false, inf, false); - addOpTest(false, -inf, false); - addOpTest(false, nan, false); - - addOpTest("Infinity", inf, true); - addOpTest("Infinity", -inf, false); - addOpTest("Infinity", nan, false); - addOpTest("infinity", inf, true); - addOpTest("infinity", -inf, false); - addOpTest("infinity", nan, false); - addOpTest("-Infinity", inf, false); - addOpTest("-Infinity", -inf, true); - addOpTest("-Infinity", nan, false); - addOpTest("-infinity", inf, false); - addOpTest("-infinity", -inf, true); - addOpTest("-infinity", nan, false); - addOpTest("NaN", inf, false); - addOpTest("NaN", -inf, false); - addOpTest("NaN", nan, true); - addOpTest("nan", inf, false); - addOpTest("nan", -inf, false); - addOpTest("nan", nan, true); - - addOpTest(inf, "abc", false); - addOpTest(inf, " ", false); - addOpTest(inf, "", false); - addOpTest(inf, "0", false); - addOpTest(-inf, "abc", false); - addOpTest(-inf, " ", false); - addOpTest(-inf, "", false); - addOpTest(-inf, "0", false); - addOpTest(nan, "abc", false); - addOpTest(nan, " ", false); - addOpTest(nan, "", false); - addOpTest(nan, "0", false); + addOpTest(inf, inf); + addOpTest(-inf, -inf); + addOpTest(nan, nan); + addOpTest(inf, -inf); + addOpTest(-inf, inf); + addOpTest(inf, nan); + addOpTest(nan, inf); + addOpTest(-inf, nan); + addOpTest(nan, -inf); + + addOpTest(5, inf); + addOpTest(5, -inf); + addOpTest(5, nan); + addOpTest(0, nan); + + addOpTest(true, "true"); + addOpTest("true", true); + addOpTest(false, "false"); + addOpTest("false", false); + addOpTest(false, "true"); + addOpTest("true", false); + addOpTest(true, "false"); + addOpTest("false", true); + addOpTest(true, "TRUE"); + addOpTest("TRUE", true); + addOpTest(false, "FALSE"); + addOpTest("FALSE", false); + + addOpTest(true, "00001"); + addOpTest("00001", true); + addOpTest(true, "00000"); + addOpTest("00000", true); + addOpTest(false, "00000"); + addOpTest("00000", false); + + addOpTest("true", 1); + addOpTest(1, "true"); + addOpTest("true", 0); + addOpTest(0, "true"); + addOpTest("false", 0); + addOpTest(0, "false"); + addOpTest("false", 1); + addOpTest(1, "false"); + + addOpTest("true", "TRUE"); + addOpTest("true", "FALSE"); + addOpTest("false", "FALSE"); + addOpTest("false", "TRUE"); + + addOpTest(true, inf); + addOpTest(true, -inf); + addOpTest(true, nan); + addOpTest(false, inf); + addOpTest(false, -inf); + addOpTest(false, nan); + + addOpTest("Infinity", inf); + addOpTest("Infinity", -inf); + addOpTest("Infinity", nan); + addOpTest("infinity", inf); + addOpTest("infinity", -inf); + addOpTest("infinity", nan); + addOpTest("-Infinity", inf); + addOpTest("-Infinity", -inf); + addOpTest("-Infinity", nan); + addOpTest("-infinity", inf); + addOpTest("-infinity", -inf); + addOpTest("-infinity", nan); + addOpTest("NaN", inf); + addOpTest("NaN", -inf); + addOpTest("NaN", nan); + addOpTest("nan", inf); + addOpTest("nan", -inf); + addOpTest("nan", nan); + + addOpTest(inf, "abc"); + addOpTest(inf, " "); + addOpTest(inf, ""); + addOpTest(inf, "0"); + addOpTest(-inf, "abc"); + addOpTest(-inf, " "); + addOpTest(-inf, ""); + addOpTest(-inf, "0"); + addOpTest(nan, "abc"); + addOpTest(nan, " "); + addOpTest(nan, ""); + addOpTest(nan, "0"); } TEST_F(LLVMCodeBuilderTest, Yield) From cdbba2e2065cd40d266578619be2adacf6e10628 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 21:55:40 +0200 Subject: [PATCH 16/22] Add more comparison edge case tests --- test/scratch_classes/value_test.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index 87d8d382..6a918f72 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -2606,15 +2606,33 @@ TEST(ValueTest, ComparisonOperators) ASSERT_FALSE(v1 == v3); ASSERT_TRUE(v1 != v3); + ASSERT_FALSE(v1 > v3); + ASSERT_TRUE(v1 < v3); ASSERT_FALSE(v1 == v4); ASSERT_TRUE(v1 != v4); + ASSERT_TRUE(v1 > v4); + ASSERT_FALSE(v1 < v4); ASSERT_FALSE(v1 == v5); ASSERT_TRUE(v1 != v5); + ASSERT_TRUE(v1 > v5); + ASSERT_FALSE(v1 < v5); ASSERT_FALSE(v2 == v5); ASSERT_TRUE(v2 != v5); + ASSERT_FALSE(v2 > v5); + ASSERT_TRUE(v2 < v5); + } + + { + Value v1 = 0; + Value v2(std::numeric_limits::quiet_NaN()); + + ASSERT_FALSE(v1 == v2); + ASSERT_TRUE(v1 != v2); + ASSERT_FALSE(v1 > v2); + ASSERT_TRUE(v1 < v2); } { From d146cf3c128fb114734d6dc4994470b12cde758b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 21:56:49 +0200 Subject: [PATCH 17/22] LLVMCodeBuilder: Implement greater than comparison --- src/dev/engine/internal/icodebuilder.h | 1 + src/dev/engine/internal/llvmcodebuilder.cpp | 47 ++++- src/dev/engine/internal/llvmcodebuilder.h | 2 + test/dev/llvm/llvmcodebuilder_test.cpp | 181 ++++++++++++++++++++ test/mocks/codebuildermock.h | 1 + 5 files changed, 227 insertions(+), 5 deletions(-) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index 6fd80778..353ab798 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -30,6 +30,7 @@ class ICodeBuilder virtual void createDiv() = 0; virtual void createCmpEQ() = 0; + virtual void createCmpGT() = 0; virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index cc51e404..9026e6ad 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -136,6 +136,14 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::CmpGT: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0].second; + const auto &arg2 = step.args[1].second; + step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::GT); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -479,6 +487,11 @@ void LLVMCodeBuilder::createCmpEQ() createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, 2); } +void LLVMCodeBuilder::createCmpGT() +{ + createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, 2); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); @@ -955,11 +968,17 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr arg1, s type2 = Compiler::StaticType::Number; // Optimize number and bool comparison - if (type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::Bool) + int optNumberBool = 0; + + if (type1 == Compiler::StaticType::Number && type2 == Compiler::StaticType::Bool) { type2 = Compiler::StaticType::Number; + optNumberBool = 2; // operand 2 was bool + } - if (type1 == Compiler::StaticType::Bool && type2 == Compiler::StaticType::Number) + if (type1 == Compiler::StaticType::Bool && type2 == Compiler::StaticType::Number) { type1 = Compiler::StaticType::Number; + optNumberBool = 1; // operand 1 was bool + } if (type1 != type2 || type1 == Compiler::StaticType::Unknown || type2 == Compiler::StaticType::Unknown) { // If the types are different or at least one of them @@ -997,8 +1016,25 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr arg1, s return m_builder.CreateSelect(nan, m_builder.getInt1(true), cmp); } - case Comparison::GT: - return m_builder.CreateFCmpOGT(value1, value2); + case Comparison::GT: { + llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN + llvm::Value *cmp = m_builder.CreateFCmpOGT(value1, value2); + llvm::Value *nan; + llvm::Value *nanCmp; + + if (optNumberBool == 1) { + nan = isNaN(value2); + nanCmp = castValue(arg1, Compiler::StaticType::Bool); + } else if (optNumberBool == 2) { + nan = isNaN(value1); + nanCmp = m_builder.CreateNot(castValue(arg2, Compiler::StaticType::Bool)); + } else { + nan = isNaN(value1); + nanCmp = m_builder.CreateFCmpUGT(value1, value2); + } + + return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); + } case Comparison::LT: return m_builder.CreateFCmpOLT(value1, value2); @@ -1016,7 +1052,8 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr arg1, s return m_builder.CreateICmpEQ(value1, value2); case Comparison::GT: - return m_builder.CreateICmpSGT(value1, value2); + // value1 && !value2 + return m_builder.CreateAnd(value1, m_builder.CreateNot(value2)); case Comparison::LT: return m_builder.CreateICmpSLT(value1, value2); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 6cec0cc6..8f61aeec 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -32,6 +32,7 @@ class LLVMCodeBuilder : public ICodeBuilder void createDiv() override; void createCmpEQ() override; + void createCmpGT() override; void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -69,6 +70,7 @@ class LLVMCodeBuilder : public ICodeBuilder Mul, Div, CmpEQ, + CmpGT, Yield, BeginIf, BeginElse, diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 82bc89d9..553ce67c 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -595,6 +595,187 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison) addOpTest(nan, "0"); } +TEST_F(LLVMCodeBuilderTest, GreaterThanComparison) +{ + auto addOpTest = [this](Value v1, Value v2) { + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createCmpGT(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + callConstFuncForType(v1.type()); + m_builder->addConstValue(v2); + callConstFuncForType(v2.type()); + m_builder->createCmpGT(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(v1 > v2).toString() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; + + addOpTest(10, 10); + addOpTest(10, 8); + addOpTest(8, 10); + + addOpTest(-4.25, -4.25); + addOpTest(-4.25, 5.312); + addOpTest(5.312, -4.25); + + addOpTest(true, true); + addOpTest(true, false); + addOpTest(false, true); + + addOpTest(1, true); + addOpTest(1, false); + + addOpTest("abC def", "abC def"); + addOpTest("abC def", "abc dEf"); + addOpTest("abC def", "ghi Jkl"); + addOpTest("ghi Jkl", "abC def"); + addOpTest("abC def", "hello world"); + + addOpTest(" ", ""); + addOpTest(" ", "0"); + addOpTest(" ", 0); + addOpTest(0, " "); + addOpTest("", "0"); + addOpTest("", 0); + addOpTest(0, ""); + addOpTest("0", 0); + addOpTest(0, "0"); + + addOpTest(5.25, "5.25"); + addOpTest("5.25", 5.25); + addOpTest(5.25, " 5.25"); + addOpTest(" 5.25", 5.25); + addOpTest(5.25, "5.25 "); + addOpTest("5.25 ", 5.25); + addOpTest(5.25, " 5.25 "); + addOpTest(" 5.25 ", 5.25); + addOpTest(5.25, "5.26"); + addOpTest("5.26", 5.25); + addOpTest("5.25", "5.26"); + addOpTest(5, "5 "); + addOpTest("5 ", 5); + addOpTest(0, "1"); + addOpTest("1", 0); + addOpTest(0, "test"); + addOpTest("test", 0); + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(inf, inf); + addOpTest(-inf, -inf); + addOpTest(nan, nan); + addOpTest(inf, -inf); + addOpTest(-inf, inf); + addOpTest(inf, nan); + addOpTest(nan, inf); + addOpTest(-inf, nan); + addOpTest(nan, -inf); + + addOpTest(5, inf); + addOpTest(inf, 5); + addOpTest(5, -inf); + addOpTest(-inf, 5); + addOpTest(5, nan); + addOpTest(nan, 5); + addOpTest(0, nan); + addOpTest(nan, 0); + + addOpTest(true, "true"); + addOpTest("true", true); + addOpTest(false, "false"); + addOpTest("false", false); + addOpTest(false, "true"); + addOpTest("true", false); + addOpTest(true, "false"); + addOpTest("false", true); + addOpTest(true, "TRUE"); + addOpTest("TRUE", true); + addOpTest(false, "FALSE"); + addOpTest("FALSE", false); + + addOpTest(true, "00001"); + addOpTest("00001", true); + addOpTest(true, "00000"); + addOpTest("00000", true); + addOpTest(false, "00000"); + addOpTest("00000", false); + + addOpTest("true", 1); + addOpTest(1, "true"); + addOpTest("true", 0); + addOpTest(0, "true"); + addOpTest("false", 0); + addOpTest(0, "false"); + addOpTest("false", 1); + addOpTest(1, "false"); + + addOpTest("true", "TRUE"); + addOpTest("true", "FALSE"); + addOpTest("false", "FALSE"); + addOpTest("false", "TRUE"); + + addOpTest(true, inf); + addOpTest(inf, true); + addOpTest(true, -inf); + addOpTest(-inf, true); + addOpTest(true, nan); + addOpTest(nan, true); + addOpTest(false, inf); + addOpTest(inf, false); + addOpTest(false, -inf); + addOpTest(-inf, false); + addOpTest(false, nan); + addOpTest(nan, false); + + addOpTest("Infinity", inf); + addOpTest("Infinity", -inf); + addOpTest("Infinity", nan); + addOpTest("infinity", inf); + addOpTest("infinity", -inf); + addOpTest("infinity", nan); + addOpTest("-Infinity", inf); + addOpTest("-Infinity", -inf); + addOpTest("-Infinity", nan); + addOpTest("-infinity", inf); + addOpTest("-infinity", -inf); + addOpTest("-infinity", nan); + addOpTest("NaN", inf); + addOpTest("NaN", -inf); + addOpTest("NaN", nan); + addOpTest("nan", inf); + addOpTest("nan", -inf); + addOpTest("nan", nan); + + addOpTest(inf, "abc"); + addOpTest(inf, " "); + addOpTest(inf, ""); + addOpTest(inf, "0"); + addOpTest(-inf, "abc"); + addOpTest(-inf, " "); + addOpTest(-inf, ""); + addOpTest(-inf, "0"); + addOpTest(nan, "abc"); + addOpTest(nan, " "); + addOpTest(nan, ""); + addOpTest(nan, "0"); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index 473ded0d..43a604e9 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -20,6 +20,7 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createDiv, (), (override)); MOCK_METHOD(void, createCmpEQ, (), (override)); + MOCK_METHOD(void, createCmpGT, (), (override)); MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From 379a12e307ac98dade77c0db01dd527fe2ddc717 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 22:06:44 +0200 Subject: [PATCH 18/22] LLVMCodeBuilder: Implement lower than comparison --- src/dev/engine/internal/icodebuilder.h | 2 ++ src/dev/engine/internal/llvmcodebuilder.cpp | 37 +++++++++++++++++++-- src/dev/engine/internal/llvmcodebuilder.h | 3 ++ test/dev/llvm/llvmcodebuilder_test.cpp | 30 +++++++++++++++-- test/mocks/codebuildermock.h | 2 ++ 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index 353ab798..dc3850d6 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -31,6 +31,8 @@ class ICodeBuilder virtual void createCmpEQ() = 0; virtual void createCmpGT() = 0; + virtual void createCmpLT() = 0; + virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 9026e6ad..cd2a232f 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -144,6 +144,14 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::CmpLT: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0].second; + const auto &arg2 = step.args[1].second; + step.functionReturnReg->value = createComparison(arg1, arg2, Comparison::LT); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -492,6 +500,11 @@ void LLVMCodeBuilder::createCmpGT() createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, 2); } +void LLVMCodeBuilder::createCmpLT() +{ + createOp(Step::Type::CmpLT, Compiler::StaticType::Bool, 2); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); @@ -1036,8 +1049,25 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr arg1, s return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); } - case Comparison::LT: - return m_builder.CreateFCmpOLT(value1, value2); + case Comparison::LT: { + llvm::Value *bothNan = m_builder.CreateAnd(isNaN(value1), isNaN(value2)); // NaN == NaN + llvm::Value *cmp = m_builder.CreateFCmpOLT(value1, value2); + llvm::Value *nan; + llvm::Value *nanCmp; + + if (optNumberBool == 1) { + nan = isNaN(value2); + nanCmp = m_builder.CreateNot(castValue(arg1, Compiler::StaticType::Bool)); + } else if (optNumberBool == 2) { + nan = isNaN(value1); + nanCmp = castValue(arg2, Compiler::StaticType::Bool); + } else { + nan = isNaN(value2); + nanCmp = m_builder.CreateFCmpULT(value1, value2); + } + + return m_builder.CreateAnd(m_builder.CreateNot(bothNan), m_builder.CreateSelect(nan, nanCmp, cmp)); + } default: assert(false); @@ -1056,7 +1086,8 @@ llvm::Value *LLVMCodeBuilder::createComparison(std::shared_ptr arg1, s return m_builder.CreateAnd(value1, m_builder.CreateNot(value2)); case Comparison::LT: - return m_builder.CreateICmpSLT(value1, value2); + // value2 && !value1 + return m_builder.CreateAnd(value2, m_builder.CreateNot(value1)); default: assert(false); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 8f61aeec..39cbf623 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -33,6 +33,8 @@ class LLVMCodeBuilder : public ICodeBuilder void createCmpEQ() override; void createCmpGT() override; + void createCmpLT() override; + void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -71,6 +73,7 @@ class LLVMCodeBuilder : public ICodeBuilder Div, CmpEQ, CmpGT, + CmpLT, Yield, BeginIf, BeginElse, diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 553ce67c..98c4cb83 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -595,9 +595,10 @@ TEST_F(LLVMCodeBuilderTest, EqualComparison) addOpTest(nan, "0"); } -TEST_F(LLVMCodeBuilderTest, GreaterThanComparison) +TEST_F(LLVMCodeBuilderTest, GreaterAndLowerThanComparison) { auto addOpTest = [this](Value v1, Value v2) { + // GT createBuilder(true); m_builder->addConstValue(v1); @@ -622,7 +623,32 @@ TEST_F(LLVMCodeBuilderTest, GreaterThanComparison) code->run(ctx.get()); const std::string quotes1 = v1.isString() ? "\"" : ""; const std::string quotes2 = v2.isString() ? "\"" : ""; - ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "GT: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + + // LT + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createCmpLT(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + callConstFuncForType(v1.type()); + m_builder->addConstValue(v2); + callConstFuncForType(v2.type()); + m_builder->createCmpLT(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + str = Value(v1 < v2).toString() + '\n'; + expected = str + str; + + code = m_builder->finalize(); + ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "LT: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; }; addOpTest(10, 10); diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index 43a604e9..c2560c12 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -21,6 +21,8 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createCmpEQ, (), (override)); MOCK_METHOD(void, createCmpGT, (), (override)); + MOCK_METHOD(void, createCmpLT, (), (override)); + MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From e6edce7e728fb53f6af5e87dad952855c0778ac6 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 15 Oct 2024 22:48:03 +0200 Subject: [PATCH 19/22] Compiler: Add methods for comparison operators --- include/scratchcpp/dev/compiler.h | 4 +++ src/dev/engine/compiler.cpp | 18 +++++++++++++ test/dev/compiler/compiler_test.cpp | 39 +++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/include/scratchcpp/dev/compiler.h b/include/scratchcpp/dev/compiler.h index e98b1605..82cc3dc6 100644 --- a/include/scratchcpp/dev/compiler.h +++ b/include/scratchcpp/dev/compiler.h @@ -53,6 +53,10 @@ class LIBSCRATCHCPP_EXPORT Compiler void createMul(); void createDiv(); + void createCmpEQ(); + void createCmpGT(); + void createCmpLT(); + void moveToIf(std::shared_ptr substack); void moveToIfElse(std::shared_ptr substack1, std::shared_ptr substack2); void moveToRepeatLoop(std::shared_ptr substack); diff --git a/src/dev/engine/compiler.cpp b/src/dev/engine/compiler.cpp index bb999224..4293341b 100644 --- a/src/dev/engine/compiler.cpp +++ b/src/dev/engine/compiler.cpp @@ -126,6 +126,24 @@ void Compiler::createDiv() impl->builder->createDiv(); } +/*! Creates an equality comparison instruction using the last 2 values. */ +void Compiler::createCmpEQ() +{ + impl->builder->createCmpEQ(); +} + +/*! Creates a greater than comparison instruction using the last 2 values. */ +void Compiler::createCmpGT() +{ + impl->builder->createCmpGT(); +} + +/*! Creates a lower than comparison instruction using the last 2 values. */ +void Compiler::createCmpLT() +{ + impl->builder->createCmpLT(); +} + /*! Jumps to the given if substack. */ void Compiler::moveToIf(std::shared_ptr substack) { diff --git a/test/dev/compiler/compiler_test.cpp b/test/dev/compiler/compiler_test.cpp index 1b2044ac..2a0089d2 100644 --- a/test/dev/compiler/compiler_test.cpp +++ b/test/dev/compiler/compiler_test.cpp @@ -271,6 +271,45 @@ TEST_F(CompilerTest, CreateDiv) compile(compiler, block); } +TEST_F(CompilerTest, CreateCmpEQ) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createCmpEQ); + compiler->createCmpEQ(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateCmpGT) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createCmpGT); + compiler->createCmpGT(); + }); + + compile(compiler, block); +} + +TEST_F(CompilerTest, CreateCmpLT) +{ + Compiler compiler(&m_engine, &m_target); + auto block = std::make_shared("", ""); + + block->setCompileFunction([](Compiler *compiler) { + EXPECT_CALL(*m_builder, createCmpLT); + compiler->createCmpLT(); + }); + + compile(compiler, block); +} + TEST_F(CompilerTest, MoveToIf) { Compiler compiler(&m_engine, &m_target); From e1ad826b9ff7951def3411e1b2b009557bc191a8 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:10:17 +0200 Subject: [PATCH 20/22] Fix conversion of NaN to bool --- src/scratch/value_functions.cpp | 2 +- test/scratch_classes/value_test.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/scratch/value_functions.cpp b/src/scratch/value_functions.cpp index b7b678b7..f8763200 100644 --- a/src/scratch/value_functions.cpp +++ b/src/scratch/value_functions.cpp @@ -238,7 +238,7 @@ extern "C" if (v->type == ValueType::Bool) { return v->boolValue; } else if (v->type == ValueType::Number) { - return v->numberValue != 0; + return v->numberValue != 0 && !std::isnan(v->numberValue); } else if (v->type == ValueType::String) { return value_stringToBool(v->stringValue); } else { diff --git a/test/scratch_classes/value_test.cpp b/test/scratch_classes/value_test.cpp index 6a918f72..f2e24d7e 100644 --- a/test/scratch_classes/value_test.cpp +++ b/test/scratch_classes/value_test.cpp @@ -1152,6 +1152,15 @@ TEST(ValueTest, ToBool) v = -2.54; ASSERT_EQ(v.toBool(), true); + v = std::numeric_limits::infinity(); + ASSERT_EQ(v.toBool(), true); + + v = -std::numeric_limits::infinity(); + ASSERT_EQ(v.toBool(), true); + + v = std::numeric_limits::quiet_NaN(); + ASSERT_EQ(v.toBool(), false); + v = false; ASSERT_EQ(v.toBool(), false); v = true; From eca7a73246bfd3c329c2963b1c0cb44234199679 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 16 Oct 2024 10:11:51 +0200 Subject: [PATCH 21/22] LLVMCodeBuilder: Implement 'and' and 'or' gates --- src/dev/engine/internal/icodebuilder.h | 2 + src/dev/engine/internal/llvmcodebuilder.cpp | 48 ++++-- src/dev/engine/internal/llvmcodebuilder.h | 6 +- test/dev/llvm/llvmcodebuilder_test.cpp | 160 ++++++++++++++++++++ test/mocks/codebuildermock.h | 2 + 5 files changed, 208 insertions(+), 10 deletions(-) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index dc3850d6..be921b2d 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -33,6 +33,8 @@ class ICodeBuilder virtual void createCmpGT() = 0; virtual void createCmpLT() = 0; + virtual void createAnd() = 0; + virtual void createOr() = 0; virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index cd2a232f..47707e15 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -152,6 +152,26 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::And: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0]; + const auto &arg2 = step.args[1]; + llvm::Value *bool1 = castValue(arg1.second, arg1.first); + llvm::Value *bool2 = castValue(arg2.second, arg2.first); + step.functionReturnReg->value = m_builder.CreateAnd(bool1, bool2); + break; + } + + case Step::Type::Or: { + assert(step.args.size() == 2); + const auto &arg1 = step.args[0]; + const auto &arg2 = step.args[1]; + llvm::Value *bool1 = castValue(arg1.second, arg1.first); + llvm::Value *bool2 = castValue(arg2.second, arg2.first); + step.functionReturnReg->value = m_builder.CreateOr(bool1, bool2); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -472,37 +492,47 @@ void LLVMCodeBuilder::addListContents(List *list) void LLVMCodeBuilder::createAdd() { - createOp(Step::Type::Add, Compiler::StaticType::Number, 2); + createOp(Step::Type::Add, Compiler::StaticType::Number, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createSub() { - createOp(Step::Type::Sub, Compiler::StaticType::Number, 2); + createOp(Step::Type::Sub, Compiler::StaticType::Number, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createMul() { - createOp(Step::Type::Mul, Compiler::StaticType::Number, 2); + createOp(Step::Type::Mul, Compiler::StaticType::Number, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createDiv() { - createOp(Step::Type::Div, Compiler::StaticType::Number, 2); + createOp(Step::Type::Div, Compiler::StaticType::Number, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createCmpEQ() { - createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, 2); + createOp(Step::Type::CmpEQ, Compiler::StaticType::Bool, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createCmpGT() { - createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, 2); + createOp(Step::Type::CmpGT, Compiler::StaticType::Bool, Compiler::StaticType::Number, 2); } void LLVMCodeBuilder::createCmpLT() { - createOp(Step::Type::CmpLT, Compiler::StaticType::Bool, 2); + createOp(Step::Type::CmpLT, Compiler::StaticType::Bool, Compiler::StaticType::Number, 2); +} + +void LLVMCodeBuilder::createAnd() +{ + createOp(Step::Type::And, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 2); +} + +void LLVMCodeBuilder::createOr() +{ + createOp(Step::Type::Or, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 2); } void LLVMCodeBuilder::beginIfStatement() @@ -881,7 +911,7 @@ llvm::Value *LLVMCodeBuilder::removeNaN(llvm::Value *num) return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_ctx, llvm::APFloat(0.0)), num); } -void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, size_t argCount) +void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount) { Step step(type); @@ -889,7 +919,7 @@ void LLVMCodeBuilder::createOp(Step::Type type, Compiler::StaticType retType, si size_t j = 0; for (size_t i = m_tmpRegs.size() - argCount; i < m_tmpRegs.size(); i++) - step.args.push_back({ Compiler::StaticType::Number, m_tmpRegs[i] }); + step.args.push_back({ argType, m_tmpRegs[i] }); m_tmpRegs.erase(m_tmpRegs.end() - argCount, m_tmpRegs.end()); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 39cbf623..86b5d1ec 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -35,6 +35,8 @@ class LLVMCodeBuilder : public ICodeBuilder void createCmpGT() override; void createCmpLT() override; + void createAnd() override; + void createOr() override; void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -74,6 +76,8 @@ class LLVMCodeBuilder : public ICodeBuilder CmpEQ, CmpGT, CmpLT, + And, + Or, Yield, BeginIf, BeginElse, @@ -150,7 +154,7 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::Value *isNaN(llvm::Value *num); llvm::Value *removeNaN(llvm::Value *num); - void createOp(Step::Type type, Compiler::StaticType retType, size_t argCount); + void createOp(Step::Type type, Compiler::StaticType retType, Compiler::StaticType argType, size_t argCount); llvm::Value *createValue(std::shared_ptr reg); llvm::Value *createComparison(std::shared_ptr arg1, std::shared_ptr arg2, Comparison type); diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 98c4cb83..68afd160 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -802,6 +802,166 @@ TEST_F(LLVMCodeBuilderTest, GreaterAndLowerThanComparison) addOpTest(nan, "0"); } +TEST_F(LLVMCodeBuilderTest, AndOr) +{ + auto addOpTest = [this](Value v1, Value v2) { + // And + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createAnd(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + callConstFuncForType(v1.type()); + m_builder->addConstValue(v2); + callConstFuncForType(v2.type()); + m_builder->createAnd(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(v1.toBool() && v2.toBool()).toString() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes1 = v1.isString() ? "\"" : ""; + const std::string quotes2 = v2.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "AND: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + + // Or + createBuilder(true); + + m_builder->addConstValue(v1); + m_builder->addConstValue(v2); + m_builder->createOr(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v1); + callConstFuncForType(v1.type()); + m_builder->addConstValue(v2); + callConstFuncForType(v2.type()); + m_builder->createOr(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + str = Value(v1.toBool() || v2.toBool()).toString() + '\n'; + expected = str + str; + + code = m_builder->finalize(); + ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "OR: " << quotes1 << v1.toString() << quotes1 << " " << quotes2 << v2.toString() << quotes2; + }; + + addOpTest(10, 8); + addOpTest(-4.25, -4.25); + addOpTest(-4.25, 5.312); + + addOpTest(true, true); + addOpTest(true, false); + addOpTest(false, true); + + addOpTest(1, true); + addOpTest(1, false); + + addOpTest("abc", "def"); + addOpTest("true", "true"); + addOpTest("true", "false"); + addOpTest("false", "true"); + addOpTest("false", "false"); + + addOpTest(5.25, "5.25"); + addOpTest("5.25", 5.25); + addOpTest(0, "1"); + addOpTest("1", 0); + addOpTest(0, "test"); + addOpTest("test", 0); + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(inf, inf); + addOpTest(-inf, -inf); + addOpTest(nan, nan); + addOpTest(inf, -inf); + addOpTest(-inf, inf); + addOpTest(inf, nan); + addOpTest(nan, inf); + addOpTest(-inf, nan); + addOpTest(nan, -inf); + + addOpTest(true, "true"); + addOpTest("true", true); + addOpTest(false, "false"); + addOpTest("false", false); + addOpTest(false, "true"); + addOpTest("true", false); + addOpTest(true, "false"); + addOpTest("false", true); + addOpTest(true, "TRUE"); + addOpTest("TRUE", true); + addOpTest(false, "FALSE"); + addOpTest("FALSE", false); + + addOpTest(true, "00001"); + addOpTest("00001", true); + addOpTest(true, "00000"); + addOpTest("00000", true); + addOpTest(false, "00000"); + addOpTest("00000", false); + + addOpTest("true", 1); + addOpTest(1, "true"); + addOpTest("true", 0); + addOpTest(0, "true"); + addOpTest("false", 0); + addOpTest(0, "false"); + addOpTest("false", 1); + addOpTest(1, "false"); + + addOpTest("true", "TRUE"); + addOpTest("true", "FALSE"); + addOpTest("false", "FALSE"); + addOpTest("false", "TRUE"); + + addOpTest(true, inf); + addOpTest(inf, true); + addOpTest(true, -inf); + addOpTest(-inf, true); + addOpTest(true, nan); + addOpTest(nan, true); + addOpTest(false, inf); + addOpTest(inf, false); + addOpTest(false, -inf); + addOpTest(-inf, false); + addOpTest(false, nan); + addOpTest(nan, false); + + addOpTest("Infinity", inf); + addOpTest("Infinity", -inf); + addOpTest("Infinity", nan); + addOpTest("infinity", inf); + addOpTest("infinity", -inf); + addOpTest("infinity", nan); + addOpTest("-Infinity", inf); + addOpTest("-Infinity", -inf); + addOpTest("-Infinity", nan); + addOpTest("-infinity", inf); + addOpTest("-infinity", -inf); + addOpTest("-infinity", nan); + addOpTest("NaN", inf); + addOpTest("NaN", -inf); + addOpTest("NaN", nan); + addOpTest("nan", inf); + addOpTest("nan", -inf); + addOpTest("nan", nan); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index c2560c12..f1fb3f9c 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -23,6 +23,8 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createCmpGT, (), (override)); MOCK_METHOD(void, createCmpLT, (), (override)); + MOCK_METHOD(void, createAnd, (), (override)); + MOCK_METHOD(void, createOr, (), (override)); MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override)); From 4d9f6fa30a5f24e2019a49308c3f0188d3205e54 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:05:09 +0200 Subject: [PATCH 22/22] LLVMCodeBuilder: Implement not gate --- src/dev/engine/internal/icodebuilder.h | 2 + src/dev/engine/internal/llvmcodebuilder.cpp | 13 +++++ src/dev/engine/internal/llvmcodebuilder.h | 3 + test/dev/llvm/llvmcodebuilder_test.cpp | 62 +++++++++++++++++++++ test/mocks/codebuildermock.h | 2 + 5 files changed, 82 insertions(+) diff --git a/src/dev/engine/internal/icodebuilder.h b/src/dev/engine/internal/icodebuilder.h index be921b2d..f5852098 100644 --- a/src/dev/engine/internal/icodebuilder.h +++ b/src/dev/engine/internal/icodebuilder.h @@ -35,6 +35,8 @@ class ICodeBuilder virtual void createAnd() = 0; virtual void createOr() = 0; + virtual void createNot() = 0; + virtual void beginIfStatement() = 0; virtual void beginElseBranch() = 0; virtual void endIf() = 0; diff --git a/src/dev/engine/internal/llvmcodebuilder.cpp b/src/dev/engine/internal/llvmcodebuilder.cpp index 47707e15..b35efa2b 100644 --- a/src/dev/engine/internal/llvmcodebuilder.cpp +++ b/src/dev/engine/internal/llvmcodebuilder.cpp @@ -172,6 +172,14 @@ std::shared_ptr LLVMCodeBuilder::finalize() break; } + case Step::Type::Not: { + assert(step.args.size() == 1); + const auto &arg = step.args[0]; + llvm::Value *value = castValue(arg.second, arg.first); + step.functionReturnReg->value = m_builder.CreateNot(value); + break; + } + case Step::Type::Yield: if (!m_warp) { freeHeap(); @@ -535,6 +543,11 @@ void LLVMCodeBuilder::createOr() createOp(Step::Type::Or, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 2); } +void LLVMCodeBuilder::createNot() +{ + createOp(Step::Type::Not, Compiler::StaticType::Bool, Compiler::StaticType::Bool, 1); +} + void LLVMCodeBuilder::beginIfStatement() { Step step(Step::Type::BeginIf); diff --git a/src/dev/engine/internal/llvmcodebuilder.h b/src/dev/engine/internal/llvmcodebuilder.h index 86b5d1ec..5fb17df5 100644 --- a/src/dev/engine/internal/llvmcodebuilder.h +++ b/src/dev/engine/internal/llvmcodebuilder.h @@ -37,6 +37,8 @@ class LLVMCodeBuilder : public ICodeBuilder void createAnd() override; void createOr() override; + void createNot() override; + void beginIfStatement() override; void beginElseBranch() override; void endIf() override; @@ -78,6 +80,7 @@ class LLVMCodeBuilder : public ICodeBuilder CmpLT, And, Or, + Not, Yield, BeginIf, BeginElse, diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index 68afd160..6715cc0e 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -962,6 +962,68 @@ TEST_F(LLVMCodeBuilderTest, AndOr) addOpTest("nan", nan); } +TEST_F(LLVMCodeBuilderTest, Not) +{ + auto addOpTest = [this](Value v) { + createBuilder(true); + + m_builder->addConstValue(v); + m_builder->createNot(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + m_builder->addConstValue(v); + callConstFuncForType(v.type()); + m_builder->createNot(); + m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }); + + std::string str = Value(!v.toBool()).toString() + '\n'; + std::string expected = str + str; + + auto code = m_builder->finalize(); + auto ctx = code->createExecutionContext(&m_target); + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + const std::string quotes = v.isString() ? "\"" : ""; + ASSERT_THAT(testing::internal::GetCapturedStdout(), Eq(expected)) << "NOT: " << quotes << v.toString() << quotes; + }; + + addOpTest(10); + addOpTest(-4.25); + addOpTest(5.312); + addOpTest(1); + addOpTest(0); + + addOpTest(true); + addOpTest(false); + + addOpTest("abc"); + addOpTest("5.25"); + addOpTest("0"); + + static const double inf = std::numeric_limits::infinity(); + static const double nan = std::numeric_limits::quiet_NaN(); + + addOpTest(inf); + addOpTest(-inf); + addOpTest(nan); + + addOpTest("true"); + addOpTest("false"); + addOpTest("TRUE"); + addOpTest("FALSE"); + + addOpTest("00001"); + addOpTest("00000"); + + addOpTest("Infinity"); + addOpTest("infinity"); + addOpTest("-Infinity"); + addOpTest("-infinity"); + addOpTest("NaN"); + addOpTest("nan"); +} + TEST_F(LLVMCodeBuilderTest, Yield) { auto build = [this]() { diff --git a/test/mocks/codebuildermock.h b/test/mocks/codebuildermock.h index f1fb3f9c..3271451d 100644 --- a/test/mocks/codebuildermock.h +++ b/test/mocks/codebuildermock.h @@ -25,6 +25,8 @@ class CodeBuilderMock : public ICodeBuilder MOCK_METHOD(void, createAnd, (), (override)); MOCK_METHOD(void, createOr, (), (override)); + MOCK_METHOD(void, createNot, (), (override)); + MOCK_METHOD(void, beginIfStatement, (), (override)); MOCK_METHOD(void, beginElseBranch, (), (override)); MOCK_METHOD(void, endIf, (), (override));