diff options
| Commit | 0ee080abc8a84c0d9f489b7c782448188d8fd0cf (patch) | |
|---|---|---|
| Author | 2018-11-27 09:41:24 +0200 | |
| Committer | 2018-11-27 09:41:24 +0200 | |
| Files | bbb343f6198ada4d413169f853d8a301ecbdce66 | |
| IPL-0ee080abc8a84c0d9f489b7c782448188d8fd0cf.tar IPL-0ee080abc8a84c0d9f489b7c782448188d8fd0cf.tar.gz IPL-0ee080abc8a84c0d9f489b7c782448188d8fd0cf.zip | ||
| Parent | f6cdfe77480452f152b916e129fa7ae06ed1a37b (diff) | |
| Parent | 43b6cf4a91e0222d9840ae4474df38f93dd13ac5 (diff) | |
Merge pull request #73 from SofiaCPP/feature/values
Made spasm to work with nan-boxed values
6 files changed, 269 insertions, 151 deletions
| -rw-r--r-- | JSImpl/test/empty.cpp | 78 | ||||
| -rw-r--r-- | spasm/src/main.cpp | 68 | ||||
| -rw-r--r-- | spasm/src/spasm.cpp | 88 | ||||
| -rw-r--r-- | spasm/src/spasm.hpp | 1 | ||||
| -rw-r--r-- | spasm/src/types.hpp | 4 | ||||
| -rw-r--r-- | spasm/src/value.hpp | 181 |
diff --git a/JSImpl/test/empty.cpp b/JSImpl/test/empty.cpp index 2725467..8bf7d13 100644 --- a/JSImpl/test/empty.cpp +++ b/JSImpl/test/empty.cpp @@ -1,6 +1,7 @@ #include <gtest/gtest.h> #include <memory> #include <cmath> +#include <spasm.hpp> TEST(Empty, Empty) { @@ -60,73 +61,6 @@ TEST(Pointer, int64) ASSERT_EQ(data + 9, PC); } -/* - * Value will be either a double, undefined, null, string, array, object, - * function, boolean. - * - * Pointer values (like string, array, object, function) can be shared pointers - * via intrusive pointer implementation. This way we can get a reference - * counting garbage collection. - * - */ -struct Value -{ - Value(double v) - { - m_value.as_double = v; - } - - Value(int tag, void* pointer) - { - assert(tag && "Zero tag is plain double"); - assert(!((size_t)pointer >> 48) && "Use only pointers to the heap."); - m_value.as_pointer.pointer = (size_t)pointer; - m_value.as_pointer.tag = tag; - m_value.as_pointer.nan = 0x1fff; - } - struct NanPointer - { - size_t pointer:48; - size_t tag:3; - size_t nan:13; - }; - - struct CheckType - { - size_t payload: 48; - size_t check:16; - }; - union { - double as_double; - NanPointer as_pointer; - CheckType to_check; - - } m_value; - - static_assert(sizeof(size_t) == 8, "unsupported arch"); - static_assert(sizeof(double) == 8, "unsupported arch"); - - bool is_double() const - { - return m_value.to_check.check <= 0xFFF8; - } - - double get_double() const - { - assert(is_double()); - return m_value.as_double; - } - - int get_tag() { - assert(!is_double()); - return m_value.as_pointer.tag; - } - - void* get_pointer() const { - return (void*)(size_t) m_value.as_pointer.pointer; - } -}; - TEST(NaNBox, PlainDoubles) { @@ -136,11 +70,11 @@ TEST(NaNBox, PlainDoubles) }; for (auto value: values) { - Value box(value); + Spasm::Value box(value); ASSERT_TRUE(box.is_double()); ASSERT_EQ(value, box.get_double()); - Value box2(-value); + Spasm::Value box2(-value); ASSERT_TRUE(box2.is_double()); ASSERT_EQ(-value, box2.get_double()); } @@ -148,14 +82,14 @@ TEST(NaNBox, PlainDoubles) TEST(NaNBox, Border) { - Value v(2, nullptr); + Spasm::Value v(Spasm::ValueType::Object, nullptr); ASSERT_TRUE(std::isnan(v.m_value.as_double)); ASSERT_FALSE(std::isinf(v.m_value.as_double)); } TEST(NaNBox, IsNaN) { - Value v2(std::sqrt(-1.0)); + Spasm::Value v2(std::sqrt(-1.0)); ASSERT_TRUE(v2.is_double()); } @@ -171,7 +105,7 @@ TEST(NaNBox, Pointers) for (auto& value: pointers) { - Value box(1, value.get()); + Spasm::Value box(Spasm::ValueType::Object, value.get()); ASSERT_FALSE(box.is_double()); ASSERT_EQ(value.get(), box.get_pointer()); } diff --git a/spasm/src/main.cpp b/spasm/src/main.cpp index a6465a6..7c423cb 100644 --- a/spasm/src/main.cpp +++ b/spasm/src/main.cpp @@ -1,34 +1,34 @@ -#include <fstream> -#include <iostream> - -#include <memory> -#include "spasm.hpp" - -int main(int argc, const char* argv[]) -{ - if (argc != 2) - return 1; - - std::ifstream input(argv[1], std::ios_base::in | std::ios_base::binary); - - size_t len; - - input.read((char*)&len, sizeof(len)); - - std::unique_ptr<Spasm::byte[]> bytecode(new Spasm::byte[len]); - - input.read((char*)bytecode.get(), len); - - for (size_t i = 0; i < len; ++i) - std::cout << std::hex << (int)bytecode[i] << ' '; - std::cout << std::endl; - - Spasm::Spasm vm; - vm.Initialize(len, bytecode.get()); - - vm.run(); - - std::cout << std::endl; - - return 0; -} +#include <fstream>
+#include <iostream>
+
+#include <memory>
+#include "spasm.hpp"
+
+int main(int argc, const char* argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ std::ifstream input(argv[1], std::ios_base::in | std::ios_base::binary);
+
+ size_t len;
+
+ input.read((char*)&len, sizeof(len));
+
+ std::unique_ptr<Spasm::byte[]> bytecode(new Spasm::byte[len]);
+
+ input.read((char*)bytecode.get(), len);
+
+ for (size_t i = 0; i < len; ++i)
+ std::cout << std::hex << (int)bytecode[i] << ' ';
+ std::cout << std::endl;
+
+ Spasm::Spasm vm;
+ vm.Initialize(len, bytecode.get());
+
+ vm.run();
+
+ std::cout << std::endl;
+
+ return 0;
+}
diff --git a/spasm/src/spasm.cpp b/spasm/src/spasm.cpp index 4d30946..63e09a0 100644 --- a/spasm/src/spasm.cpp +++ b/spasm/src/spasm.cpp @@ -76,7 +76,7 @@ Spasm::RunResult Spasm::run() { const auto count = PC_t(read_reg(size)); assert(m_SP + count <= &data_stack[data_stack.size() - 1]); - std::fill(m_SP, m_SP + count, 0); + std::fill(m_SP, m_SP + count, data_t{}); m_SP += count; break; } @@ -187,38 +187,38 @@ Spasm::RunResult Spasm::run() lesseq(arg0, arg1, arg2); break; } - case OpCodes::Greater: - { - const auto arg0 = read_reg(size); - const auto arg1 = read_reg(size); - const auto arg2 = read_reg(size); - greater(arg0, arg1, arg2); - break; - } - case OpCodes::GreaterEq: - { - const auto arg0 = read_reg(size); - const auto arg1 = read_reg(size); - const auto arg2 = read_reg(size); - greatereq(arg0, arg1, arg2); - break; - } - case OpCodes::Equal: - { - const auto arg0 = read_reg(size); - const auto arg1 = read_reg(size); - const auto arg2 = read_reg(size); - equal(arg0, arg1, arg2); - break; - } - case OpCodes::NotEqual: - { - const auto arg0 = read_reg(size); - const auto arg1 = read_reg(size); - const auto arg2 = read_reg(size); - not_equal(arg0, arg1, arg2); - break; - } + case OpCodes::Greater: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + greater(arg0, arg1, arg2); + break; + } + case OpCodes::GreaterEq: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + greatereq(arg0, arg1, arg2); + break; + } + case OpCodes::Equal: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + equal(arg0, arg1, arg2); + break; + } + case OpCodes::NotEqual: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + not_equal(arg0, arg1, arg2); + break; + } default: { std::cerr << opcode << ": not implemented" << std::endl; @@ -312,7 +312,7 @@ void Spasm::divide(reg_t a0, reg_t a1, reg_t a2) */ void Spasm::modulus(reg_t a0, reg_t a1, reg_t a2) { - set_local(a0, data_t(int64_t(get_local(a1)) % int64_t(get_local(a2)))); + set_local(a0, get_local(a1) % get_local(a2)); } /*! @@ -357,7 +357,7 @@ void Spasm::go(reg_t a0) void Spasm::call(reg_t a0) { Frame call{m_PC, PC_t(m_FP - &data_stack[0]), - m_SP - &data_stack[0] - PC_t(*(m_SP - 1)) - 1}; + m_SP - &data_stack[0] - PC_t((*(m_SP - 1)).get_double()) - 1}; m_Frames.push(std::move(call)); m_FP = m_SP - 1; go(a0); @@ -414,22 +414,22 @@ void Spasm::lesseq(reg_t a0, reg_t a1, reg_t a2) void Spasm::greater(reg_t a0, reg_t a1, reg_t a2) { - set_local(a0, get_local(a1) > get_local(a2)); + set_local(a0, get_local(a1) > get_local(a2)); } void Spasm::greatereq(reg_t a0, reg_t a1, reg_t a2) { - set_local(a0, get_local(a1) >= get_local(a2)); + set_local(a0, get_local(a1) >= get_local(a2)); } void Spasm::equal(reg_t a0, reg_t a1, reg_t a2) { - set_local(a0, fabs(get_local(a1) - get_local(a2)) < 0.0001); + set_local(a0, get_local(a1) == get_local(a2)); } void Spasm::not_equal(reg_t a0, reg_t a1, reg_t a2) { - set_local(a0, fabs(get_local(a1) - get_local(a2)) > 0.0001); + set_local(a0, get_local(a1) != get_local(a2)); } data_t Spasm::get_local(reg_t reg) @@ -493,28 +493,28 @@ data_t Spasm::read_number(size_t size) switch (size) { case 0: - return m_ByteCode[m_PC++]; + return data_t(double(m_ByteCode[m_PC++])); case 1: { auto result = *reinterpret_cast<int16_t*>(&m_ByteCode[0] + m_PC); m_PC += 2; - return result; + return data_t(double(result)); } case 2: { auto result = *reinterpret_cast<int32_t*>(&m_ByteCode[0] + m_PC); m_PC += 4; - return result; + return data_t(double(result)); } case 3: { auto result = *reinterpret_cast<double*>(&m_ByteCode[0] + m_PC); m_PC += 8; - return result; + return data_t(double(result)); } } assert(false && "not reached"); - return 0; + return data_t{}; } } // namespace SpasmImpl diff --git a/spasm/src/spasm.hpp b/spasm/src/spasm.hpp index 01015d3..da3f44f 100644 --- a/spasm/src/spasm.hpp +++ b/spasm/src/spasm.hpp @@ -2,6 +2,7 @@ #define SPASM_HPP
#include "spasm_impl.hpp"
+#include "value.hpp"
namespace Spasm
{
diff --git a/spasm/src/types.hpp b/spasm/src/types.hpp index 59562e1..a5d45d8 100644 --- a/spasm/src/types.hpp +++ b/spasm/src/types.hpp @@ -5,11 +5,13 @@ #include <stack> #include <vector> +#include "value.hpp" + namespace SpasmImpl { typedef int8_t byte; typedef size_t PC_t; -typedef double data_t; +typedef Spasm::Value data_t; class Spasm; typedef void (Spasm::*Operation)(); diff --git a/spasm/src/value.hpp b/spasm/src/value.hpp new file mode 100644 index 0000000..6569721 --- /dev/null +++ b/spasm/src/value.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include <cassert> + +namespace Spasm +{ + enum class ValueType + { + Number, + Null, + Undefined, + Boolean, + String, + Array, + Object, + Function, + }; + + static_assert(int(ValueType::Function) < 8, "Too many types"); + +struct Value +{ + Value() + { + m_value.as_double = 0.0; + } + + explicit Value(double v) + { + m_value.as_double = v; + } + + explicit Value(bool v) + : Value(ValueType::Boolean, (size_t)v) + + { + } + + Value(ValueType tag, size_t payload) + { + assert(tag != ValueType::Number && "Zero tag is plain double"); + assert(!(payload >> 48) && "Use only pointers to the heap."); + m_value.as_pointer.pointer = payload; + m_value.as_pointer.tag = size_t(tag); + m_value.as_pointer.nan = 0x1fff; + } + + Value(ValueType tag, void* pointer) + : Value(tag, (size_t)pointer) + { + } + + struct NanPointer + { + size_t pointer:48; + size_t tag:3; + size_t nan:13; + }; + + struct CheckType + { + size_t payload: 48; + size_t check:16; + }; + union { + double as_double; + NanPointer as_pointer; + CheckType to_check; + size_t as_int64; + } m_value; + + static_assert(sizeof(size_t) == 8, "unsupported arch"); + static_assert(sizeof(double) == 8, "unsupported arch"); + + bool is_double() const + { + return m_value.to_check.check <= 0xFFF8; + } + + double get_double() const + { + assert(is_double()); + return m_value.as_double; + } + + ValueType get_type() const { + return is_double()? ValueType::Number : ValueType(m_value.as_pointer.tag); + } + + void* get_pointer() const { + return (void*)(size_t) m_value.as_pointer.pointer; + } + + bool get_boolean() const { + assert(get_type() == ValueType::Boolean); + return m_value.as_pointer.pointer != 0; + } + + explicit operator bool() const { + // NaN will be true. Assume that is ok. + return (get_type() != ValueType::Boolean && m_value.as_int64 != 0) || get_boolean(); + } + +}; + +inline std::istream& operator>>(std::istream& input, Value& value) +{ + return input >> value.m_value.as_double; +} + +inline std::ostream& operator<<(std::ostream& output, const Value& value) +{ + switch (value.get_type())
+ {
+ case ValueType::Number:
+ return output << value.m_value.as_double; + case ValueType::Boolean:
+ return output << bool(value); + default: + break;
+ } + return output; +} + +inline Value operator+(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() + rhs.get_double()); +} + +inline Value operator-(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() - rhs.get_double()); +} + +inline Value operator*(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() * rhs.get_double()); +} + +inline Value operator/(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() / rhs.get_double()); +} + +inline Value operator%(const Value& lhs, const Value& rhs) +{ + return Value(double(int64_t(lhs.get_double()) % int64_t(rhs.get_double()))); +} + + +inline Value operator<(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() < rhs.get_double()); +} + +inline Value operator>(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() > rhs.get_double()); +} + +inline Value operator<=(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() <= rhs.get_double()); +} + +inline Value operator>=(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() >= rhs.get_double()); +} + +inline Value operator==(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() == rhs.get_double()); +} + +inline Value operator!=(const Value& lhs, const Value& rhs) +{ + return Value(lhs.get_double() != rhs.get_double()); +} + +}
\ No newline at end of file |
