diff options
| Commit | f6cdfe77480452f152b916e129fa7ae06ed1a37b (patch) | |
|---|---|---|
| Author | 2018-11-21 21:22:14 +0200 | |
| Committer | 2018-11-26 20:30:23 +0200 | |
| Files | 16953826a3a3dade76f192438d01abd0f5706a81 /website | |
| IPL-f6cdfe77480452f152b916e129fa7ae06ed1a37b.tar IPL-f6cdfe77480452f152b916e129fa7ae06ed1a37b.tar.gz IPL-f6cdfe77480452f152b916e129fa7ae06ed1a37b.zip | ||
| Parent | cf3ccded52fde55247fee2dffa83778e82c88387 (diff) | |
Slides for NaN Boxing
2 files changed, 302 insertions, 0 deletions
| -rw-r--r-- | website/content/post/contents.md | 3 | ||||
| -rw-r--r-- | website/content/slides/06_vm_values/_index.md | 299 |
diff --git a/website/content/post/contents.md b/website/content/post/contents.md index 902caa9..6d614d3 100644 --- a/website/content/post/contents.md +++ b/website/content/post/contents.md @@ -25,3 +25,6 @@ Table of contents for the course. 0. Intro to virtual machines - [slides]({{< ref "/slides/05_vm_intro" >}}), [doc](https://github.com/SofiaCPP/IPL/blob/master/website/content/slides/05_vm_intro/_index.md) + +0. Values in VMs - [slides]({{< ref "/slides/06_vm_values" >}}), + [doc](https://github.com/SofiaCPP/IPL/blob/master/website/content/slides/06_vm_values/_index.md) diff --git a/website/content/slides/06_vm_values/_index.md b/website/content/slides/06_vm_values/_index.md new file mode 100644 index 0000000..23d3d91 --- /dev/null +++ b/website/content/slides/06_vm_values/_index.md @@ -0,0 +1,299 @@ +--- +title: "Values in the VM" +date: 2018-11-21T17:45:53+03:00 +draft: false +outputs: ["Reveal"] +--- + +# Values in the VM + +- Languages and VMs +- NanBoxing + +--- + +# Language Semantics and VM + +The VM implementation depends a lot on language semantics + +- typing - static or dynamic? +- number types - integers and floating point +- strings - immutability + +--- + +That is why it is almost every language has its own VM. + +--- + +# Values in the VM? + + function add(a, b) { + return a + b + } + + +--- + + add r1 a1 a2 + ret r1 + +--- + +- must work for numbers +- must work for strings +- must work for strings and numbers mixed +- can work for objects and arrays + - if the target language has semantics for that + +--- + +## How do we support that? + +C++ is statically type, the value in the registers can have a single C++ type + +--- + +## Axiom of CS + +> *Every* problem can be solved by adding another level of indirection. + +> ..., except too many indirections. + +--- + +### Polymorhism + +- C++ way for type erasure + +- Base class with virtual methods + +--- + + struct Value { + virtual ValuePtr Add(ValuePtr rhs) const = 0; + // ... + } + + struct NumberValue : Value { + double m_Number; + } + +--- + +#### Performance + +- `sizeof(NumberValue)` - 16 B - 100% overhead + - pointer + double +- cache miss on every access to the number +- virtual call for adding two numbers + +- It is so inefficient, that almost nobody does it this way + +--- +### What we can do? + +--- + + struct Value { + bool m_IsDouble; + union { + double as_Double; + void* as_Pointer; + } m_Value; + } + +--- + +- still 100% memory overhead because m_Value will be aligned to 8 bytes + - will not fit in a register + - more cache misses + +--- +### Doubles and IEEE + +A double has 64-bits, from least to most significant: + +1. 52 bits - mantissa + - a leading 1 bit set to 1 is assumed, so the precision is 53 bits +2. 11 bits - exponent - from 0 to 2047 +3. 1 bit - sign + +Value = Mantissa * 2 ^ (exponent - 1023) + +--- + +Not all possible values are actually valid numbers. + +- Infinity - exponent is 2047, mantissa - 0 + + +--- + + double r = sqrt(-1); + + NaN - Not a Number + +- sign bit is set +- exponent is all 1 (2047) +- most significant bit of the mantissa is 1 +- the rest 51 bits do not matter - it is always a NaN + - the CPU always sets them to 0 + + +--- +# The rest 51 bits do not matter + +- so we can (ab)use them to store our pointers! +- but one pointer is 64 bits? + +--- + +> For x86_64 / arm64 the addresses above 2^48 are reserved and can't be used +> from user applications + +--- + +- 48 bits for the pointer +- 3 bits for ... type + +--- +### Type + + enum ValueType { + Number = 0, + Null = 1, + Undefined = 2, + Boolean = 3, + String = 4, + Array = 5, + Object = 6, + Function= 7, + }; + +--- +#### Why Number is 0? + +- so that we treat regular NaNs as numbers. + +--- +#### NanBoxing + + +Need to split easily `double` to bits - use union to share the memory between a +`double` and structs with bitfields. + + union { + double as_double; + NanPointer as_pointer; + CheckType to_check; + + } m_value; + +--- + + struct NanPointer + { + // order from least to most significant bits + // bit field - has type size_t, but only 48 bits + size_t pointer:48; + size_t tag:3; // 0 for a real nan and non zero for out type + size_t nan:13; // must be all 1s to force a nan + }; + +--- + + // Used to check whether the value is a double or not + struct CheckType + { + size_t payload: 48; + size_t check:16; + }; + +--- + + bool is_double() const + { + // If any of the least significant bits is non-zero, + // then this is not a normal double or a NaN value + 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; + } + +--- + +#? + +--- + +## Strings + +Strings in JavaScript are immutable, which means that changing one string +returns another. + + +--- + +#### String interning + +> A method of storing only one instance of any given string, which is immutable. + +This means that there is only one copy of `"answer"` inside the VM + +--- + +Comparing strings for equality becomes very fast. + +> Two strings are the same if and only if their pointers are the same. + +--- + + struct StringValue + { + std::string Value; + // extra data + // int RefCount; + // int GCFlags; + + } + +--- + class Spasm + { + typedef std::unordered_set<StringValue> StringTable; + + StringTable m_Strings; + }; + +--- + + Value Spasm::AllocateString(const char* s) { + auto current = m_Strings.find(s); + if (current != m_Strings.end()) + { + return Value(ValueType::String, &(*current)); + } + auto insert = m_Strings.insert(StringValue(s)); + return Value(ValueType::String, &(*insert.second)); + } + + +--- + +# ? |
