Website Summary Files Commits Branches @commit @diff @stats
diff options
context:
space:
mode:
Commit0ee080abc8a84c0d9f489b7c782448188d8fd0cf (patch)
Author
 dtrendafilov <trendafilov.dn@gmail.com>
2018-11-27 09:41:24 +0200
Committer
 GitHub <noreply@github.com>
2018-11-27 09:41:24 +0200
Filesbbb343f6198ada4d413169f853d8a301ecbdce66
IPL-0ee080abc8a84c0d9f489b7c782448188d8fd0cf.tar
IPL-0ee080abc8a84c0d9f489b7c782448188d8fd0cf.tar.gz
IPL-0ee080abc8a84c0d9f489b7c782448188d8fd0cf.zip
Parentf6cdfe77480452f152b916e129fa7ae06ed1a37b (diff)
Parent43b6cf4a91e0222d9840ae4474df38f93dd13ac5 (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.cpp78
-rw-r--r--spasm/src/main.cpp68
-rw-r--r--spasm/src/spasm.cpp88
-rw-r--r--spasm/src/spasm.hpp1
-rw-r--r--spasm/src/types.hpp4
-rw-r--r--spasm/src/value.hpp181
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