Upgrade to Pro — share decks privately, control downloads, hide ads and more …

StaticScript - TypeScript compiler on top of TypeScript as frontend and LLVM as backend #2

StaticScript - TypeScript compiler on top of TypeScript as frontend and LLVM as backend #2

Это второй доклад из серии докладов которые будут посвящены разработке компилятора “TypeScript” используя LLVM в качестве backend, и TypeScript в качестве frontend.

Dmitry Patsura

January 17, 2019
Tweet

More Decks by Dmitry Patsura

Other Decks in Programming

Transcript

  1. • Геометрии и число • Как устроенны Branches/Cycles • ГПСЧ/ГСЧ

    • С++ 11 random • Math.random and JS engines • JS engines and code structure • Array • Dynamic value Поговорим сегодня:
  2. { function doMath(): number { const a = 5.5; const

    b = 14.5; return ((a + b) * 50) / 10; } puts("hello"); puts(doMath()); }
  3. Calculate PI number R R S(circle) = S(square) = 2R

    2R 4 * R2 π * R2 π * R2 4 * R2 P = = π 4 π = 4 * P
  4. { function calculatePI(cycles: number): number { let inside = 0;

    for (let i = 0; i < cycles; i++) { let x = Math.random(); let y = Math.random(); if ((x*x + y*y) < 1) { inside++ } } return 4.0 * inside / cycles; } console_log(calculatePI(1000000000)); }
  5. +23 #include <cstdio> int main() { auto a = 5;

    auto b = 4; if (a > b) { puts("5 > 4"); } else { puts("5 < 4"); } } clang -S -emit-llvm main.cpp -O0 Branches
  6. @.str = private unnamed_addr constant [6 x i8] c"5 >

    4\00", align 1 @.str.1 = private unnamed_addr constant [6 x i8] c"5 < 4\00", align 1 define i32 @main() #0 { %a.0 = alloca i32, align 4 %b.0 = alloca i32, align 4 store i32 5, i32* %a.0, align 4 store i32 4, i32* %b.0, align 4 %a.1 = load i32, i32* %a.0, align 4 %b.1 = load i32, i32* %b.0, align 4 %6 = icmp sgt i32 %a.1, %b.1 br i1 %6, label %7, label %9 ; <label>:7: ; preds = %0 %8 = call i32 @puts(i8* @.str) br label %11 ; <label>:9: ; preds = %0 %10 = call i32 @puts(i8* @.str.1)) br label %11 ; <label>:11: ; preds = %9, %7 ret i32 0 } if (a > b) { puts("5 > 4"); puts("5 < 4"); auto a = 5; auto b = 4;
  7. import * as ts from "typescript"; import * as llvm

    from 'llvm-node'; import {NodeGenerateInterface} from "../node-generate.interface"; import {Context} from "../context"; class IfStatementCodeGenerator implements NodeGenerateInterface<ts.IfStatement, void> { generate(node: ts.IfStatement, ctx: Context, builder: llvm.IRBuilder): void { // @todo Будем писать код тут } }
  8. generate(node: ts.IfStatement, ctx: Context, builder: llvm.IRBuilder): void { const positiveBlock

    = llvm.BasicBlock.create(ctx.llvmContext, "if.true"); ctx.scope.enclosureFunction.llvmFunction.addBasicBlock(positiveBlock); const negativeBlock = llvm.BasicBlock.create(ctx.llvmContext, "if.false"); ctx.scope.enclosureFunction.llvmFunction.addBasicBlock(negativeBlock); const next = llvm.BasicBlock.create(ctx.llvmContext, "if.end"); ctx.scope.enclosureFunction.llvmFunction.addBasicBlock(next); }
  9. generate(node: ts.IfStatement, ctx: Context, builder: llvm.IRBuilder): void { const positiveBlock

    = llvm.BasicBlock.create(ctx.llvmContext, "if.true"); ctx.scope.enclosureFunction.llvmFunction.addBasicBlock(positiveBlock); const negativeBlock = llvm.BasicBlock.create(ctx.llvmContext, "if.false"); ctx.scope.enclosureFunction.llvmFunction.addBasicBlock(negativeBlock); const next = llvm.BasicBlock.create(ctx.llvmContext, "if.end"); ctx.scope.enclosureFunction.llvmFunction.addBasicBlock(next); emitCondition( node.expression, ctx, builder, positiveBlock, negativeBlock ); }
  10. export function emitCondition( condition: ts.Expression, ctx: Context, builder: llvm.IRBuilder, positiveBlock:

    llvm.BasicBlock, negativeBlock: llvm.BasicBlock, ) { const left = buildFromExpression(condition, ctx, builder); const conditionBoolValue = left.toBoolean(ctx, builder, condition); builder.createCondBr(conditionBoolValue.getValue(), positiveBlock, negativeBlock); } if (expression) {
  11. generate(node: ts.IfStatement, ctx: Context, builder: llvm.IRBuilder): void { //.. ..

    .. emitCondition( node.expression, ctx, builder, positiveBlock, negativeBlock ); builder.setInsertionPoint(positiveBlock); passNode(node.thenStatement, ctx, builder); builder.createBr(next); builder.setInsertionPoint(negativeBlock); passNode(node.elseStatement, ctx, builder); builder.createBr(next); builder.setInsertionPoint(next); }
  12. ovr  hlvm   master • ?  ./bin/ssc

    --printIR sandbox/do-simple-math.ts ✔ define i64 @main() { entry: %a = alloca double store double 5.000000e+00, double* %a %b = alloca double store double 4.000000e+00, double* %b %0 = load double, double* %a %1 = load double, double* %b %cmpGT = fcmp ogt double %0, %1 br i1 %cmpGT, label %if.true, label %if.false if.true: ; preds = %entry call void @puts(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) br label %if.end if.end: ; preds = %if.true, %if.false ret i64 0 if.false: ; preds = %Entry call void @puts(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0)) br label %if.end }
  13. +32 int main() { for (auto i = 0; i

    < 100; i++) { } } clang -S -emit-llvm main.cpp -O0 Cycles
  14. define i32 @main() #0 { %1 = alloca i32, align

    4 %2 = alloca i32, align 4 store i32 0, i32* %1, align 4 store i32 0, i32* %2, align 4 br label %3 ; <label>:3: ; preds = %7, %0 %4 = load i32, i32* %2, align 4 %5 = icmp slt i32 %4, 100 br i1 %5, label %6, label %10 ; <label>:6: ; preds = %3 br label %7 ; <label>:7: ; preds = %6 %8 = load i32, i32* %2, align 4 %9 = add nsw i32 %8, 1 store i32 %9, i32* %2, align 4 br label %3 ; <label>:10: ; preds = %3 %11 = load i32, i32* %1, align 4 ret i32 %11 }
  15. define i64 @main() { entry: %i = alloca double store

    double 0.000000e+00, double* %i br label %for.condition for.body: ; preds = %for.condition br label %for.inc for.condition: ; preds = %for.inc, %entry %0 = load double, double* %i %cmpLT = fcmp olt double %0, 1.000000e+03 br i1 %cmpLT, label %for.body, label %next for.inc: ; preds = %for.body %1 = load double, double* %i %2 = fadd double %1, 1.000000e+00 store double %2, double* %i br label %for.condition next: ; preds = %for.condition ret i64 0 }
  16. i condition -> i = 0 0 T for.body 1

    T for.body 2 T for.body 100 F next
  17. Math.Random https://www.ecma-international.org/ecma-262/5.1/#sec-15.8.2.14 15.8.2.14 random ( ) Returns a Number value

    with positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy. This function takes no arguments. • Нет алгоритма • Распределение? • Chosen randomly or pseudo randomly
  18. randomly / pseudo randomly Источники случайных чисел физические процессы другие

    явления • Дробовой шум • Радиоактивный распад • Спонтанное параметрическое рассеяние • Тепловой шум в резисторе • Атмосферный шум (random.org) • Разница в скорости хода часов • Времени между нажатиями на кнопки • Шума со встроенного микрофона • Использование сети или интернета
  19. randomly / pseudo randomly Энтропия /dev/random Счётчик тактов процессора, однако

    собирается только во время аппаратных прерываний Java SecureRandom Взаимодействие между потоками Microsoft CryptoAPI Текущее время, размер жёсткого диска, размер свободной памяти, номер процесса и NETBIOS-имя компьютера
  20. +45 randomly / pseudo randomly Почему бы не добавить инструкцию

    в CPU вместе с бекдором от производителя?
  21. +46 randomly / pseudo randomly RDRAND (Intel Ivy Bridge+, AMD

    2015 +) RDSEED (Intel Broadwell+, AMD Zen +)
  22. https://www.amd.com/system/files/TechDocs/24594.pdf Loads the destination register with a hardware-generated random value.

    The size of the returned value in bits is determined by the size of the destination register.
  23. Простейший ГСЧ с источником энтропии ovr@MacBook-Pro-Dmitry  ~/projects/ovr/hlvm  

    logo-introduce • ?  node -i  ✔ > function random_piterjs(max) { return new Date().getTime() % max; } undefined > random_piterjs(100); 18 > random_piterjs(100); 13 > random_piterjs(100); 48 > random_piterjs(100); 69 > random_piterjs(100); 73 > random_piterjs(100); 32 > random_piterjs(100); 67
  24. Math.Random random_device std::random_device генератор равномерно распределенных целых случайных чисел. Производит

    истинно случайные числа если недетерминированный источник (например, аппаратное устройство) доступен для реализации.
  25. Math.Random static std::random_device rd; static std::mt19937 mt(rd()); static std::uniform_real_distribution<double> dist(0,

    1.0); LIBRARY_EXPORT double Math__random() { return dist(mt); } ГСЧ = ГПСЧ с источником энтропии
  26. Math.random and JS Engines/V8 Modern JS engine parser ast wasm

    compiler runtime Builtins Base •Platform •Utils •HasMap •CPU •Stdlib
  27. // ES6 #sec-math.random TF_BUILTIN(MathRandom, CodeStubAssembler) { Node* context = Parameter(Descriptor::kContext);

    Node* native_context = LoadNativeContext(context); // Load cache index. TVARIABLE(Smi, smi_index); smi_index = CAST( LoadContextElement(native_context, Context::MATH_RANDOM_INDEX_INDEX)); // Cached random numbers are exhausted if index is 0. Go to slow path. Label if_cached(this); GotoIf(SmiAbove(smi_index.value(), SmiConstant(0)), &if_cached); // Cache exhausted, populate the cache. Return value is the new index. Node* const refill_math_random = ExternalConstant(ExternalReference::refill_math_random()); //… } https://v8.dev/docs/csa-builtins https://github.com/v8/v8/blob/master/src/builtins/builtins-math-gen.h
  28. https://v8.dev/docs/torque V8 Torque is a language that allows developers contributing

    to the V8 project to express changes in the VM by focusing on their intent of their changes to the VM, rather than preoccupying themselves with unrelated implementation details.
  29. Math.random and JS Engines Смотришь refill_math_random FixedDoubleArray cache = FixedDoubleArray::cast(native_context->math_random_cache());

    // Create random numbers. for (int i = 0; i < kCacheSize; i++) { // Generate random numbers using xorshift128+. base::RandomNumberGenerator::XorShift128(&state.s0, &state.s1); cache->set(i, base::RandomNumberGenerator::ToDouble(state.s0)); } pod->set(0, state); Smi new_index = Smi::FromInt(kCacheSize); native_context->set_math_random_index(new_index);
  30. base/utils/random-number-generator RandomNumberGenerator::RandomNumberGenerator() { #if V8_OS_CYGWIN || V8_OS_WIN // Use rand_s()

    to gather entropy on Windows. See: // https://code.google.com/p/v8/issues/detail?id=2905 unsigned first_half, second_half; errno_t result = rand_s(&first_half); DCHECK_EQ(0, result); result = rand_s(&second_half); DCHECK_EQ(0, result); SetSeed((static_cast<int64_t>(first_half) << 32) + second_half); #else // Gather entropy from /dev/urandom if available. FILE* fp = fopen("/dev/urandom", "rb"); if (fp != nullptr) { int64_t seed; size_t n = fread(&seed, sizeof(seed), 1, fp); fclose(fp); if (n == 1) { SetSeed(seed); return; }
  31. base/utils/random-number-generator // We cannot assume that random() or rand() were

    seeded // properly, so instead of relying on random() or rand(), // we just seed our PRNG using timing data as fallback. // This is weak entropy, but it's sufficient, because // it is the responsibility of the embedder to install // an entropy source using v8::V8::SetEntropySource(), // which provides reasonable entropy, see: // https://code.google.com/p/v8/issues/detail?id=2905 int64_t seed = Time::NowFromSystemTime().ToInternalValue() << 24; seed ^= TimeTicks::HighResolutionNow().ToInternalValue() << 16; seed ^= TimeTicks::Now().ToInternalValue() << 8; SetSeed(seed);
  32. Math.random and JS Engines В void RandomNumberGenerator::SetSeed(int64_t seed) { initial_seed_

    = seed; state0_ = MurmurHash3(bit_cast<uint64_t>(seed)); state1_ = MurmurHash3(~state0_); CHECK(state0_ != 0 || state1_ != 0); } uint64_t RandomNumberGenerator::MurmurHash3(uint64_t h) { h ^= h >> 33; h *= uint64_t{0xFF51AFD7ED558CCD}; h ^= h >> 33; h *= uint64_t{0xC4CEB9FE1A85EC53}; h ^= h >> 33; return h; } https://github.com/aappleby/smhasher
  33. Math.random and JS Engines Левый // Static and exposed for

    external use. static inline double ToDouble(uint64_t state0) { // Exponent for double values for [1.0 .. 2.0) static const uint64_t kExponentBits = uint64_t{0x3FF0000000000000}; uint64_t random = (state0 >> 12) | kExponentBits; return bit_cast<double>(random) - 1; } // Static and exposed for external use. static inline void XorShift128(uint64_t* state0, uint64_t* state1) { uint64_t s1 = *state0; uint64_t s0 = *state1; *state0 = s0; s1 ^= s1 << 23; s1 ^= s1 >> 17; s1 ^= s0; s1 ^= s0 >> 26; *state1 = s1; }
  34. Math.random and JS Engines Угол XorShift128 Xorshift random number generators

    are a class of pseudorandom number generators that were discovered by George Marsaglia • Marsaglia's theorem • Multiply-with-carry • Subtract-with-borrow • Kiss • Mother methods for random • Ziggurat algorithm
  35. ChakraCore-мертвеченный https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Library/MathLibrary.cpp ///---------------------------------------------------------------------------- /// Random() returns a random number 0

    <= n < 1.0, as described in /// (ES5.0: S15.8.2.14). ///---------------------------------------------------------------------------- Var Math::Random(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); AssertMsg(callInfo.Count > 0, "Should always have implicit 'this'"); ScriptContext* scriptContext = function->GetScriptContext(); Assert(!(callInfo.Flags & CallFlags_New)); double res = JavascriptMath::Random(scriptContext); return JavascriptNumber::ToVarNoCheck(res, scriptContext); }
  36. ChakraCore-мертвеченный https://github.com/Microsoft/ChakraCore/blob/master/lib/Runtime/Math/JavascriptMath.cpp void InitializeRandomSeeds(uint64 *seed0, uint64 *seed1, ScriptContext *sc) {

    LARGE_INTEGER s0; LARGE_INTEGER s1; if (!rand_s(reinterpret_cast<unsigned int*>(&s0.LowPart)) && !rand_s(reinterpret_cast<unsigned int*>(&s0.HighPart)) && !rand_s(reinterpret_cast<unsigned int*>(&s1.LowPart)) && !rand_s(reinterpret_cast<unsigned int*>(&s1.HighPart))) { *seed0 = s0.QuadPart; *seed1 = s1.QuadPart; } }
  37. double ConvertRandomSeedsToDouble(const uint64 seed0, const uint64 seed1) { const uint64

    mExp = 0x3FF0000000000000; const uint64 mMant = 0x000FFFFFFFFFFFFF; // Take lower 52 bits of the sum of two seeds to make a double // Subtract 1.0 to negate the implicit integer bit of 1. Final range: [0.0, 1.0) // See IEEE754 Double-precision floating-point format for details // https://en.wikipedia.org/wiki/Double-precision_floating-point_format uint64 resplusone_ui64 = ((seed0 + seed1) & mMant) | mExp; double res = *(reinterpret_cast<double*>(&resplusone_ui64)) - 1.0; return res; } void Xorshift128plus(uint64 *seed0, uint64 *seed1) { uint64 s1 = *seed0; uint64 s0 = *seed1; *seed0 = s0; s1 ^= s1 << 23; s1 ^= s1 >> 17; s1 ^= s0; s1 ^= s0 >> 26; *seed1 = s1; }
  38. ovr@MacBook-Pro-Dmitry  hlvm   master • ?  ./output/main

     ✔ Monte Carlo simulation [1] 63302 segmentation fault ./output/main ovr@MacBook-Pro-Dmitry  hlvm   master • ?   SIGSEGV(11) ↵
  39. ovr@  hlvm   master • ?  lldb

    ./output/main ✔ Monte Carlo simulation Process 64172 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x7ffeef3ffff8) frame #0: 0x000000010000a6b3 main`v8::base::RandomNumberGenerator::XorShift128(state0=0x000000010030006 8, state1=0x0000000100300070) at random-number-generator.h:120 117 118 // Static and exposed for external use. 119 static inline void XorShift128(uint64_t* state0, uint64_t* state1) { -> 120 uint64_t s1 = *state0; 121 uint64_t s0 = *state1; 122 *state0 = s0; 123 s1 ^= s1 << 23; Target 0: (main) stopped.
  40. define double @calculatePI(double %cycles, double %r) local_unnamed_addr { Entry: %cmpLT3

    = fcmp ogt double %cycles, 0.000000e+00 br i1 %cmpLT3, label %for.body.lr.ph, label %for.condition1._crit_edge for.body.lr.ph: ; preds = %Entry %0 = fmul double %r, %r br label %for.body for.body: ; preds = %for.body.lr.ph, %for.body %i.05 = phi double [ 0.000000e+00, %for.body.lr.ph ], [ %9, %for.body ] %inside.04 = phi double [ 0.000000e+00, %for.body.lr.ph ], [ %inside.1, %for.body ] %1 = tail call double @_Z12Math__randomv() %2 = fmul double %1, %r %3 = tail call double @_Z12Math__randomv() %4 = fmul double %3, %r %5 = fmul double %2, %2 %6 = fmul double %4, %4 %7 = fadd double %5, %6 %cmpLT2 = fcmp olt double %7, %0 %8 = fadd double %inside.04, 1.000000e+00 %inside.1 = select i1 %cmpLT2, double %8, double %inside.04 %9 = fadd double %i.05, 1.000000e+00 %cmpLT = fcmp olt double %9, %cycles br i1 %cmpLT, label %for.body, label %for.condition1._crit_edge.loopexit for.condition1._crit_edge.loopexit: ; preds = %for.body %phitmp = fmul double %inside.1, 4.000000e+00 br label %for.condition1._crit_edge for.condition1._crit_edge: ; preds = %for.condition1._crit_edge.loopexit, %Entry %inside.0.lcssa = phi double [ 0.000000e+00, %Entry ], [ %phitmp, %for.condition1._crit_edge.loopexit ] %10 = fdiv double %inside.0.lcssa, %cycles ret double %10 }
  41. define i64 @main() local_unnamed_addr { Entry: tail call void @_Z11console_logPKc(i8*

    getelementptr inbounds ([23 x i8], [23 x i8]* @0, i64 0, i64 0)) br label %for.body.i for.body.i: ; preds = %for.body.i, %Entry %i.05.i.int = phi i32 [ 0, %Entry ], [ %.int, %for.body.i ] %inside.04.i = phi double [ 0.000000e+00, %Entry ], [ %inside.1.i, %for.body.i ] %0 = tail call double @_Z12Math__randomv() %1 = fmul double %0, 6.553600e+04 %2 = tail call double @_Z12Math__randomv() %3 = fmul double %2, 6.553600e+04 %4 = fmul double %1, %1 %5 = fmul double %3, %3 %6 = fadd double %4, %5 %cmpLT2.i = fcmp olt double %6, 0x41F0000000000000 %7 = fadd double %inside.04.i, 1.000000e+00 %inside.1.i = select i1 %cmpLT2.i, double %7, double %inside.04.i %.int = add nuw nsw i32 %i.05.i.int, 1 %cmpLT.i = icmp ult i32 %.int, 100000000 br i1 %cmpLT.i, label %for.body.i, label %calculatePI.exit calculatePI.exit: ; preds = %for.body.i %phitmp.i = fmul double %inside.1.i, 4.000000e+00 %8 = fdiv double %phitmp.i, 1.000000e+08 tail call void @_Z11console_logd(double %8) ret i64 0 }
  42. ovr@MacBook-Pro-Dmitry  hlvm   master • ?  ./output/main

     ✔ Monte Carlo simulation 30 seconds….. 1 minute…. 2 minute….
  43. sudo dtruss ./output/main open_nocancel("/dev/urandom\0", 0x0, 0x1B6) = 3 0 fstat64(0x3,

    0x7FFEE6AC1878, 0x0) = 0 0 ioctl(0x3, 0x4004667A, 0x7FFEE6AC18C4) = 0 0 dtrace: error on enabled probe ID 2175 (ID 945: syscall::read_nocancel:return): invalid kernel access in action #12 at DIF offset 68 close_nocancel(0x3) = 0 0 open_nocancel("/dev/urandom\0", 0x0, 0x1B6) = 3 0 fstat64(0x3, 0x7FFEE6AC1878, 0x0) = 0 0 ioctl(0x3, 0x4004667A, 0x7FFEE6AC18C4) = 0 0 ……
  44. class Machine { protected: v8::base::RandomNumberGenerator* randomNumberGenerator = nullptr; public: static

    Machine& Instance() { static Machine s; return s; } v8::base::RandomNumberGenerator* getRandomNumberGenerator() { if (this->randomNumberGenerator) { return this->randomNumberGenerator; } return this->randomNumberGenerator = new v8::base::RandomNumberGenerator(); } };
  45. const elements = 10; const buffer = new ArrayBuffer(elements *

    16); const view = new DataView(buffer); view.setInt16(16 * 0, 120); view.setInt16(16 * 1, 120); console.log(view.getInt8(0));
  46. interface Array<T = any> {} declare type Int8Array = Array<int8>;

    declare type Uint8Array = Array<uint8>; { const values: Int8Array = [1, 2, 3, 4, 5]; }
  47. interface Int8ArrayConstructor { new(length: number): Int8Array; } declare const Int8Array:

    Int8ArrayConstructor; { let int8Array = new Int8Array(5); }
  48. Typed Array(s) C/C++ Arrays H E L L O \0

    char* str = *str *(str+1) *(str+2) *(str+3) *(str+4) *(str+5) •Fixed-size sequential collection •No size information
  49. { let a; if (Math.random() > 0.5) { a =

    5; } else { a = false; } console_log(a); }
  50. Строго типизированное struct User { char* name; int age; int

    weight; }; int getWeight(User *ptr) { return ptr->weight; } getWeight(User*): # @getWeight(User*) pushq %rbp movq %rsp, %rbp movq %rdi, -8(%rbp) movq -8(%rbp), %rdi movl 12(%rdi), %eax popq %rbp retq
  51. Строго типизированное getWeight(User*): # @getWeight(User*) pushq %rbp movq %rsp, %rbp

    movq %rdi, -8(%rbp) movq -8(%rbp), %rdi movl 12(%rdi), %eax popq %rbp retq MEMORY char* name int age; int weight;
  52. Dynamic value class Dynamic { public: Dynamic(double value) { this->d

    = value; this->type = DynamicType::DOUBLE_TYPE; } Dynamic(bool value) { this->b = value; this->type = DynamicType::BOOLEAN_TYPE; } Dynamic(int64_t value) { this->i = value; this->type = DynamicType::INT64_TYPE; } private: DynamicType type; double d; bool b; int64_t i; }; enum DynamicType: int8_t { BOOLEAN_TYPE = 1, DOUBLE_TYPE = 2, INT64_TYPE = 3, };
  53. Dynamic value enum DynamicType: int8_t { BOOLEAN_TYPE = 1, DOUBLE_TYPE

    = 2, INT64_TYPE = 3, }; class Dynamic { private: DynamicType type; double d; bool b; int64_t i; }; 1 byte 1 byte 8 byte 8 byte 1 + 8 + 1 + 8 = 18 byte sizeof(Dynamic) = 32
  54. Dynamic value enum DynamicType: int8_t { BOOLEAN_TYPE = 1, DOUBLE_TYPE

    = 2, INT64_TYPE = 3, }; class Dynamic { private: DynamicType type; double d; bool b; int64_t i; }; MEMORY 1 8 1 8 ….14 sizeof(Dynamic) = 32
  55. Dynamic value enum DynamicType: int8_t { BOOLEAN_TYPE = 1, DOUBLE_TYPE

    = 2, INT64_TYPE = 3, }; class Dynamic { private: DynamicType type; union { double d; bool b; int64_t i; }; }; MEMORY 1 8 ….7 sizeof(Dynamic) = 16
  56. { function summ(a: any, b: number) { return a +

    b; } } • a0 = load a* • a1 = load a0 (number)