pax_global_header00006660000000000000000000000064141425663320014520gustar00rootroot0000000000000052 comment=ceffd5fc62cc31f4e4786f5d5aadaf1f473aebb8 storm-lang-0.5.10/000077500000000000000000000000001414256633200136665ustar00rootroot00000000000000storm-lang-0.5.10/.gdbinit000066400000000000000000000003231414256633200153050ustar00rootroot00000000000000# Place the following line in the file ~/.gdbinit to load this file. # add-auto-load-safe-path ~/Projects/storm/.gdbinit handle SIGXFSZ nostop noprint handle SIGXCPU nostop noprint handle SIGSEGV nostop noprint storm-lang-0.5.10/.gitignore000066400000000000000000000014241414256633200156570ustar00rootroot00000000000000# Compiled source # ################### *.com *.class *.dll *.exe *.o *.so *.so.* # Temporary files # ################### *~ # Packages # ############ # it's better to unpack these files and commit the raw source # git has its own built in compression methods *.7z *.dmg *.gz *.iso *.jar *.rar *.tar *.zip # Logs and databases # ###################### *.log *.sql *.sqlite *.ncb *.suo # OS generated files # ###################### .DS_Store* ehthumbs.db Icon? Thumbs.db *.aps *.user debug release release_* slow slow_* *.ncb *.suo Tools/ build build_* log/ ReadMe.txt *.aps \#*\# # External code in other git repos. External/ mymake/ # Generated code. */Gen/* !*/Gen/.gitignore !Core/Gen/* # Documentation pack files for Storm. root/doc *_doc # Database files (sqlite) *.db storm-lang-0.5.10/.gitmodules000066400000000000000000000002041414256633200160370ustar00rootroot00000000000000[submodule "mps"] path = mps url = ../mps.git [submodule "Linux/backtrace"] path = Linux/backtrace url = ../linux/backtrace.git storm-lang-0.5.10/.myproject000066400000000000000000000143661414256633200157150ustar00rootroot00000000000000#Storm has a bit weird concept of release and debug configurations. #The default debug configuration is acutally a release configuration, #but with some debug flags added. This is since Storm is painfully slow #without compiler optimizations on, and when using other debug features #like the debug heap. There is a mode, called "slow" that will enable #the actual debug mode (i.e. no optimizations etc.) [project] #Only build targets containing .mymake-files. explicitTargets=yes [project,release,!dist] input+=Main input+=Test [project,release] execute=no [build,!slow,!release] #Always 'release' mode, except when 'slow' is specified here. all+=release all+=storm_debug [build,release] #When compiling in release mode, use link-time code generation. all+=release all+=storm_release [build,slow] all+=storm_slow # Forward build flags: [build,dist] all+=dist [build,nostatic] all+=nostatic [build,noskia] Gui+=noskia [build,cairogl] Gui+=usecairogl [build,nobacktrace] Utils+=nobacktrace # Compatibility flag. For various backwards-compatibility fixes. # Currently, we disable large file support in SQLite, since that causes issues on Ubuntu 18.04 LTS. # We will phase this out eventually. [build,compat] all+=compat [compat] define+=STORM_COMPAT [build] #Regular libraries linked into Storm and/or any storm libraries. Core+=lib Code+=lib Compiler+=lib OS+=lib Shared+=lib Gc+=lib Utils+=lib SoundLib+=lib SoundLib+=extern_lib #Libraries that need the storm preprocessor to be run. Compiler+=stormpp TestLib+=stormpp Gui+=stormpp Graphics+=stormpp Sound+=stormpp SQL+=stormpp Crypto+=stormpp #Libraries linked into shared libraries loaded by Storm at runtime. TestLib+=sharedlib Gui+=sharedlib Graphics+=sharedlib Sound+=sharedlib SQL+=sharedlib Crypto+=sharedlib #Libraries that require position independent code on some platforms. If you intend to put the compiler itself #inside a shared library, add 'Compiler+=pic' and 'Gc+=pic' here. Core+=pic OS+=pic Shared+=pic Utils+=pic [deps] TestLib+=CppTypes Gui+=CppTypes Compiler+=CppTypes Sound+=CppTypes Graphics+=CppTypes SQL+=CppTypes Crypto+=CppTypes [!dist] #To make sure everything is built. #When building for Debian we want a bit more control of what we are building. Compiler+=TestLib Compiler+=Gui Compiler+=Graphics Compiler+=Sound Compiler+=SQL Compiler+=Crypto #Selection GC to use. [build,mps] all+=mps [build,smm] all+=smm #Add variables that indicate the GC choice. We add them to the entire project, even though #it is only necessary for Gc, Test and Main [mps] gcSuffix=_mps [smm] gcSuffix=_smm [!extern_lib] #Global build parameters pch=stdafx.h include+=./ include+=../ [storm_debug] buildDir=build/ execDir=../debug/ #Turn on some runtime checks (implemented purely in Storm). define+=FAST_DEBUG [storm_debug,windows] #Generate pdb file. flags+=/Zi /Fd linkFlags+=/DEBUG /INCREMENTAL:NO /PDB:.pdb [unix] flags=-std=c++11 flags+= [storm_debug,unix] #We do not need that aggressive optimizations... opt+=-O1 #Generate debug information. flags+=-g cflags+=-g [storm_release] buildDir=release/ execDir=../release/ [storm_release,windows] #Enable link-time code generation. Too slow to use regularly, but gives good performance! flags+=/GL linkFlags+=/LTCG [storm_release,lib,windows] #Need extra flag to the linker... link=lib /LTCG /nologo /OUT: [storm_release,unix] #We do not need O3 opt=-O2 [pic,unix] #All libraries need to be compiled with the -fPIC flag. At least on X86-64. flags+=-fPIC cflags+=-fPIC [storm_slow] buildDir=slow/ execDir=../slow/ [nostatic] define+=NOSTATIC_BUILD [windows] #Tell the Win32 API we're working with UTF16. define+=_UNICODE define+=UNICODE #Compile asm files on X86 ext+=asm noIncludes+=*.asm compile+=*.asm:ml /c /nologo /Fo /safeseh /W3 /Zi [unix] flags+=-Wno-unknown-pragmas flags+=-Wno-reorder flags+=-Wno-terminate flags+=-Wno-unused-parameter flags+=-Wno-missing-field-initializers flags+=-Wno-pmf-conversions flags+=-Wno-switch flags+=-Wno-parentheses flags+=-Wno-unused-function flags+=-Wno-pragmas #Gives out of bounds warnings due to "dynamic arrays" at the end of structs. flags+=-Wno-stringop-overflow #We need to align functions to even addresses, otherwise they will be seen as vtable offsets. flags+=-falign-functions=2 #Do not export all symbols from .so-files. Storm assumes that functions and variables in different #modules are different variables and may thus contain different values. This is not the default on UNIX #systems, at least not when using GCC. flags+=-fvisibility=hidden cflags+=-fvisibility=hidden linkFlags+=-pthread ext+=s ext+=S noIncludes+=*.s noIncludes+=*.S compile+=*.s:gcc -g -c -o compile+=*.S:gcc -g -c -o [stormpp] stormpp=CppTypes stormppUses=--use stormProvides=./ stormUses=../Core/ stormppUsing=--using packagePath=../root/ docName=doc preBuild+= --template ../Core/Gen/CppTypes.cpp --out Gen/CppTypes.cpp --asm --doc preBuildCreates+=Gen/CppTypes.cpp [stormpp,windows] stormppAsmTemplate=../Core/Gen/CppVTables.VS_X86 stormppAsmOut=Gen/CppVTables.asm preBuildCreates+=Gen/CppVTables.asm [stormpp,unix] stormppAsmTemplate=../Core/Gen/CppVTables.GCC_X64 stormppAsmOut=Gen/CppVTables.s preBuildCreates+=Gen/CppVTables.s [sharedlib] packagePath=../root// docName=_doc [sharedlib,!unix] postBuild+=if not exist "" mkdir postBuild+=copy [sharedlib,unix] flags+=-fPIC cflags+=-fPIC linkFlags+=-Wl,-z,defs postBuild+=mkdir -p postBuild+=cp [sharedlib,storm_debug] libPrefix=Debug [sharedlib,storm_release] libPrefix=Release [sharedlib,storm_slow] libPrefix=Slow # No prefix on Dist releases. [sharedlib,storm_release,dist] libPrefix= #No PCH for c-files. [windows] compile+=*.c:cl /c /Fo [unix] compile+=*.c:gcc -Wno-unknown-pragmas -Wno-pragmas -std=c99 -O3 -Wno-maybe-uninitialized -c -o storm-lang-0.5.10/Code/000077500000000000000000000000001414256633200145405ustar00rootroot00000000000000storm-lang-0.5.10/Code/.mymake000066400000000000000000000000551414256633200160240ustar00rootroot00000000000000[] #Needed to mark this as a Mymake project. storm-lang-0.5.10/Code/Arena.cpp000066400000000000000000000034721414256633200163000ustar00rootroot00000000000000#include "stdafx.h" #include "Arena.h" #include "Reg.h" #include "X86/Arena.h" #include "X64/Arena.h" #include "Core/Str.h" namespace code { Arena::Arena() {} Ref Arena::external(const wchar *name, const void *ptr) const { return Ref(externalSource(name, ptr)); } RefSource *Arena::externalSource(const wchar *name, const void *ptr) const { RefSource *src = new (this) StrRefSource(name); src->setPtr(ptr); return src; } Listing *Arena::transform(Listing *src, Binary *owner) const { assert(false); return src; } void Arena::output(Listing *src, Output *to) const { assert(false); } LabelOutput *Arena::labelOutput() const { assert(false); return null; } CodeOutput *Arena::codeOutput(Binary *owner, Array *offsets, Nat size, Nat refs) const { assert(false); return null; } CodeOutput *Arena::codeOutput(Binary *owner, LabelOutput *src) const { return codeOutput(owner, src->offsets, src->size, src->refs); } void Arena::removeFnRegs(RegSet *from) const { from->remove(ptrA); from->remove(ptrB); from->remove(ptrC); } Listing *Arena::redirect(Bool member, TypeDesc *result, Array *params, Ref fn, Operand param) { assert(false); return null; } Listing *Arena::engineRedirect(TypeDesc *result, Array *params, Ref fn, Operand engine) { assert(false); return null; } Nat Arena::firstParamId(MAYBE(TypeDesc *) desc) { assert(false); if (desc) return 0; else return 1; } Operand Arena::firstParamLoc(Nat id) { assert(false); return Operand(); } #if defined(X86) && defined(WINDOWS) Arena *arena(EnginePtr e) { return new (e.v) x86::Arena(); } #elif defined(X64) && defined(POSIX) Arena *arena(EnginePtr e) { return new (e.v) x64::Arena(); } #else #error "Please note which is the default arena for your platform." #endif } storm-lang-0.5.10/Code/Arena.h000066400000000000000000000075701414256633200157500ustar00rootroot00000000000000#pragma once #include "Core/TObject.h" #include "Core/EnginePtr.h" #include "Output.h" #include "Operand.h" namespace code { STORM_PKG(core.asm); class Listing; class Binary; class RegSet; class TypeDesc; /** * An arena represents a collection of compiled code and external references for some architecture. * * Abstract class, there is one instantiation for each supported platform. */ class Arena : public ObjectOn { STORM_CLASS; public: // Create an arena. Arena(); // Create external references. Ref external(const wchar *name, const void *ptr) const; RefSource *externalSource(const wchar *name, const void *ptr) const; /** * Transform and translate code into machine code. */ // Transform the code in preparation for this backend's code generation. This is // backend-specific. 'owner' is the binary object that will be called to handle exceptions. virtual Listing *STORM_FN transform(Listing *src, MAYBE(Binary *) owner) const; // Translate a previously transformed listing into machine code for this arena. virtual void STORM_FN output(Listing *src, Output *to) const; /** * Create output objects for this backend. */ // Create an offset-computing output. virtual LabelOutput *STORM_FN labelOutput() const; // Create a code-generating output. 'lblOffsets' and 'size' are obtained through 'labelOutput'. virtual CodeOutput *STORM_FN codeOutput(Binary *owner, Array *offsets, Nat size, Nat refs) const; CodeOutput *STORM_FN codeOutput(Binary *owner, LabelOutput *src) const; // Remove all registers not preserved during a function call on this platform. This // implementation removes ptrA, ptrB and ptrC, but other Arena implementations may want to // remove others as well. virtual void STORM_FN removeFnRegs(RegSet *from) const; /** * Other backend-specific things. */ // Create a function that calls another function (optionally with a pointer sized parameter) // to figure out which function to actually call. Useful when implementing lazy compilation. // // Calls 'fn' with 'param' (always pointer-sized or empty) to compute the // actual function to call. The actual function (as well as the 'function' implemented by // the redirect) takes params as defined by 'params' and returns 'result'. // // These redirect objects are *not* platform independent! virtual Listing *STORM_FN redirect(Bool member, TypeDesc *result, Array *params, Ref fn, Operand param); // Create a function that calls another (pre-determined) function and appends an 'EnginePtr' // object as the first parameter to the other function. Calling member functions in this // manner is not supported. virtual Listing *STORM_FN engineRedirect(TypeDesc *result, Array *params, Ref fn, Operand engine); /** * Get the location of the first parameter for a function call. Assumes that a member function is called. * * The location is acquired in two steps: first, an implementation asks the ID of the * parameter location by calling the 'firstParamId(TypeDesc *)' function. This returns one * out of several possible integers describing the parameter location. The number of * possible values can be acquired by calling 'firstParamId(null)'. * * The ID can then be passed to 'firstParamLoc' to get an Operand describing the location. * * This scheme is used so that classes like VTableCalls can detect when two functions with * different return values have the same vtable stub. This allows it to re-use the stubs. */ // Get the ID of the location of the first param. virtual Nat STORM_FN firstParamId(MAYBE(TypeDesc *) desc); // Access the location of the first parameter in a function size. The returned Operand is // always pointer-sized. virtual Operand STORM_FN firstParamLoc(Nat id); }; // Create an arena for this platform. Arena *STORM_FN arena(EnginePtr e); } storm-lang-0.5.10/Code/Binary.cpp000066400000000000000000000157231414256633200165000ustar00rootroot00000000000000#include "stdafx.h" #include "Binary.h" #include "Exception.h" #include "Core/StrBuf.h" namespace code { Binary::Binary(Arena *arena, Listing *listing) { compile(arena, listing, false); } Binary::Binary(Arena *arena, Listing *listing, Bool debug) { compile(arena, listing, debug); } void Binary::compile(Arena *arena, Listing *listing, Bool debug) { Listing *tfm = arena->transform(listing, this); if (debug) PVAR(tfm); fillBlocks(tfm); LabelOutput *labels = arena->labelOutput(); arena->output(tfm, labels); fillTryBlocks(tfm, labels); if (tfm->meta().id < labels->offsets->count()) { metaOffset = labels->offsets->at(tfm->meta().id); } else { metaOffset = 0; WARNING(L"No metadata seems to have been generated by the backend."); WARNING(L"Exception cleanup will not work!"); } CodeOutput *output = arena->codeOutput(this, labels); arena->output(tfm, output); runtime::codeUpdatePtrs(output->codePtr()); set(output->codePtr(), output->tell()); } void Binary::toS(StrBuf *to) const { *to << S("Binary object:"); const nat columns = 16; const byte *code = (const byte *)address(); if (!code) { *to << S(" "); return; } nat size = runtime::codeSize(code); for (nat i = 0; i < size; i++) { if (i % columns == 0) { *to << S("\n") << hex(i) << S(" -"); } *to << S(" ") << hex(code[i]); } } const GcType Binary::blockArrayType = { GcType::tArray, null, null, sizeof(void *), 1, { 0 }, }; const GcType Binary::blockType = { GcType::tArray, null, null, sizeof(Variable), 0, {}, }; const GcType Binary::tryInfoArrayType = { GcType::tArray, null, null, sizeof(TryInfo), 1, { OFFSET_OF(TryInfo, type) }, }; void Binary::fillBlocks(Listing *src) { Array *srcBlocks = src->allBlocks(); blocks = runtime::allocArray(engine(), &blockArrayType, srcBlocks->count()); for (Nat i = 0; i < srcBlocks->count(); i++) { code::Block block = srcBlocks->at(i); Array *vars = src->allVars(block); // Count variables that need finalization. Those are the only ones we need. size_t count = 0; for (Nat j = 0; j < vars->count(); j++) if (src->freeOpt(vars->at(j)) & freeOnException) count++; Block *b = (Block *)runtime::allocArray(engine(), &blockType, count); blocks->v[i] = b; b->parent = src->parent(block).key(); size_t at = 0; for (Nat j = 0; j < vars->count(); j++) { const Var &v = vars->at(j); Nat flags = src->freeOpt(v); // We only include the ones we actually need. if ((flags & freeOnException) == 0) continue; if (flags & freePtr) { // No additional flags needed, but we set sPtr for good measure. flags |= Variable::sPtr; } else if (v.size() == Size::sPtr) { flags |= Variable::sPtr; } else if (v.size() == Size::sByte) { flags |= Variable::sByte; } else if (v.size() == Size::sInt) { flags |= Variable::sInt; } else if (v.size() == Size::sLong) { flags |= Variable::sLong; } else { throw new (this) InvalidValue(S("Can only use bytes, integers, longs and pointers for ") S("variable cleanup. Specify 'freePtr' to get a pointer to ") S("the value instead!")); } b->vars[at].id = v.key(); b->vars[at].flags = flags; at++; } } } void Binary::fillTryBlocks(Listing *src, LabelOutput *labels) { Nat count = 0; Array *blocks = src->allBlocks(); for (Nat i = 0; i < blocks->count(); i++) { if (Array *info = src->catchInfo(blocks->at(i))) count += info->count(); } if (count == 0) { tryBlocks = null; return; } tryBlocks = runtime::allocArray(engine(), &tryInfoArrayType, count); Nat at = 0; for (Nat i = 0; i < blocks->count(); i++) { code::Block b = blocks->at(i); if (Array *info = src->catchInfo(b)) { for (Nat j = 0; j < info->count(); j++) { tryBlocks->v[at].blockId = code::Block(b).key(); tryBlocks->v[at].resumeOffset = labels->offsets->at(info->at(j).resume.id); tryBlocks->v[at].type = info->at(j).type; at++; } } } } void Binary::cleanup(StackFrame &frame) { for (Nat i = frame.block; i != code::Block().key(); i = blocks->v[i]->parent) { Block *b = blocks->v[i]; // Reverse order is common. for (Nat j = b->count; j > 0; j--) { cleanup(frame, b->vars[j - 1]); } } } Nat Binary::cleanup(StackFrame &frame, Nat until) { for (Nat i = frame.block; i != code::Block().key(); i = blocks->v[i]->parent) { Block *b = blocks->v[i]; // Reverse order is common. for (Nat j = b->count; j > 0; j--) { cleanup(frame, b->vars[j - 1]); } // Done? if (i == until) return blocks->v[i]->parent; } // Outside of all blocks. return code::Block().key(); } void Binary::cleanup(StackFrame &frame, Variable &v) { if (v.flags & freeOnException) { // Element #0 is the total size, then VarCleanup-instances start. byte *data = (byte *)address() + metaOffset + sizeof(void *); VarCleanup *vars = (VarCleanup *)data; VarCleanup &now = vars[v.id]; void *freeFn = now.function; int offset = now.offset; nat activeAfter = now.activeAfter; // If not active, we don't destroy it. if (frame.activation < activeAfter) return; void *ptr = frame.toPtr(offset); if (v.flags & freeIndirection) ptr = *(void **)ptr; typedef void (*FPtr)(void *v); typedef void (*FByte)(Byte v); typedef void (*FInt)(Int v); typedef void (*FLong)(Long v); if (v.flags & freePtr) { FPtr p = (FPtr)freeFn; (*p)(ptr); } else { switch (v.flags & Variable::sMask) { case Variable::sPtr: { FPtr p = (FPtr)freeFn; (*p)(*(void **)ptr); break; } case Variable::sByte: { FByte p = (FByte)freeFn; (*p)(*(Byte *)ptr); break; } case Variable::sInt: { FInt p = (FInt)freeFn; (*p)(*(Int *)ptr); break; } case Variable::sLong: { FLong p = (FLong)freeFn; (*p)(*(Long *)ptr); break; } } } } } bool Binary::hasCatch(Nat active, RootObject *exception, Resume &resume) { struct Compare { inline bool operator() (const TryInfo &l, Nat r) const { return l.blockId < r; } }; if (!tryBlocks) return false; for (Nat block = active; block != code::Block().key(); block = blocks->v[block]->parent) { TryInfo *end = tryBlocks->v + tryBlocks->count; TryInfo *found = std::lower_bound(tryBlocks->v, end, block, Compare()); // Check all possible matches. for (; found != end && found->blockId == block; found++) { if (runtime::isA(exception, found->type)) { // Find where to resume. byte *data = (byte *)address(); resume.ip = data + found->resumeOffset; // The first entry is the total stack depth. size_t *table = (size_t *)(data + metaOffset); resume.stackDepth = table[0]; // Remember how far to clean. resume.cleanUntil = block; return true; } } } return false; } } storm-lang-0.5.10/Code/Binary.h000066400000000000000000000070771414256633200161500ustar00rootroot00000000000000#pragma once #include "Core/TObject.h" #include "Core/GcType.h" #include "Arena.h" #include "Listing.h" #include "RefSource.h" #include "StackFrame.h" namespace code { STORM_PKG(core.asm); /** * A Binary represents a Listing that has been translated into machine code, along with any * extra information needed (such as descriptions of exception handlers or similar). * * Note: The Binary class expects backends to create a table of data starting in the meta() * where each element looks like this: * size_t freeFn; - a pointer to the function to be executed when this variable is to be freed. * size_t offset; - a signed offset to the variable relative to the stack frame provided by the backend. * There shall be one entry for each variable in the Listing. Variable 0 shall be at index 0. */ class Binary : public Content { STORM_CLASS; public: // Translate a listing into machine code. STORM_CTOR Binary(Arena *arena, Listing *src); // Output the transformed ASM code for debugging. Binary(Arena *arena, Listing *src, Bool debug); // Clean up a stack frame from this function. void cleanup(StackFrame &frame); // Clean up a stack fraome from this function from the current block, up to and including // 'until'. Returns the parent of 'until', which is now to be considered the current part. Nat cleanup(StackFrame &frame, Nat until); // Check if we have any catch-clauses at all (used for pre-screening in exception handlers). inline bool hasCatch() const { return tryBlocks != null; } // Information on how to resume from a try-clause. struct Resume { // Next instruction to execute. 'null' if nothing is to be done. void *ip; // Stack depth. size_t stackDepth; // Part outside the one handling the exception. size_t cleanUntil; }; // Check if we have a catch-clause for the active part or any of its parents. bool hasCatch(Nat part, RootObject *exception, Resume &resume); // Output to string. virtual void STORM_FN toS(StrBuf *to) const; private: // Information about a single variable. struct Variable { Nat id; // The low 16 bits contain FreeOpt, the high 16 bits contain the size of the variable. Nat flags; enum { sByte = 0x1000, sInt = 0x2000, sLong = 0x3000, sPtr = 0x4000, sMask = 0xFF000 }; }; // Remember the block hierarchy. These are allocated as GcArray:s to reduce the number of allocations. struct Block { // Number of elements. const size_t count; // Parent block. size_t parent; // Variables in here. Variable vars[1]; }; // Information for try-blocks. struct TryInfo { // Current block id. Nat blockId; // Offset to continue execution from. Nat resumeOffset; // Type to catch. Type *type; }; // Offset of the metadata label. Nat metaOffset; // All parts. GcArray *blocks; // Try-block information. 'null' if no exceptions are caught. Indices do not correspond to // parts here since the array is sparse. It is necessary to perform search the array // (perhaps using a binary search). GcArray *tryBlocks; // Compile the Listing object. void compile(Arena *arena, Listing *src, Bool debug); // Fill the 'blocks' array. void fillBlocks(Listing *src); // Fill the 'tryBlocks' array if necessary. void fillTryBlocks(Listing *src, LabelOutput *labels); // Clean a single variable. void cleanup(StackFrame &frame, Variable &v); // Type declarations for the GC. static const GcType blockArrayType; static const GcType blockType; static const GcType tryInfoArrayType; }; } storm-lang-0.5.10/Code/Block.cpp000066400000000000000000000007351414256633200163030ustar00rootroot00000000000000#include "stdafx.h" #include "Block.h" #include "Core/StrBuf.h" namespace code { Block::Block() : id(-1) {} Block::Block(Nat id) : id(id) {} void Block::deepCopy(CloneEnv *) {} wostream &operator <<(wostream &to, Block l) { if (l.id == Block().id) return to << L"invalid block"; return to << L"Block" << l.id; } StrBuf &operator <<(StrBuf &to, Block l) { if (l.id == Block().id) return to << S("invalid block"); return to << S("Block") << l.id; } } storm-lang-0.5.10/Code/Block.h000066400000000000000000000017011414256633200157420ustar00rootroot00000000000000#pragma once namespace code { STORM_PKG(core.asm); /** * Represents a block of code in a listing. These blocks contain a set of local variables, along * with parent-child relations. This information will be available in the final Binary object as * well (but in another format). */ class Block { STORM_VALUE; public: // Create an invalid block. STORM_CTOR Block(); inline Bool STORM_FN operator ==(Block o) const { return id == o.id; } inline Bool STORM_FN operator !=(Block o) const { return id != o.id; } // Key. inline Nat STORM_FN key() const { return id; } // Deep copy. void STORM_FN deepCopy(CloneEnv *env); private: friend class Listing; friend class Operand; friend wostream &operator <<(wostream &to, Block l); friend StrBuf &operator <<(StrBuf &to, Block l); explicit Block(Nat id); Nat id; }; wostream &operator <<(wostream &to, Block l); StrBuf &STORM_FN operator <<(StrBuf &to, Block l); } storm-lang-0.5.10/Code/Code.h000066400000000000000000000005441414256633200155660ustar00rootroot00000000000000#pragma once #include "Utils/Utils.h" #include "Utils/Platform.h" #include "OS/Types.h" #include "Core/Storm.h" namespace code { // Use some types from Storm here as well. using namespace storm; } /** * Configuration of the code generation: */ // For X86: generate code that requires SSE3 (introduced in the P4 in 2004). #define X86_REQUIRE_SSE3 storm-lang-0.5.10/Code/CondFlag.cpp000066400000000000000000000034551414256633200167300ustar00rootroot00000000000000#include "stdafx.h" #include "CondFlag.h" namespace code { const wchar *name(CondFlag c) { switch (c) { case ifAlways: return S("always"); case ifNever: return S("never"); case ifOverflow: return S("overflow"); case ifNoOverflow: return S("no overflow"); case ifEqual: return S("equal"); case ifNotEqual: return S("not equal"); case ifBelow: return S("below"); case ifBelowEqual: return S("below/equal"); case ifAboveEqual: return S("above/equal"); case ifAbove: return S("above"); case ifLess: return S("less"); case ifLessEqual: return S("less/equal"); case ifGreaterEqual: return S("greater/equal"); case ifGreater: return S("greater"); case ifFBelow: return S("ifFBelow"); case ifFBelowEqual: return S("ifFBelowEqual"); case ifFAboveEqual: return S("ifFAboveEqual"); case ifFAbove: return S("ifFAbove"); } assert(false, L"Unknown CondFlag: " + ::toS(c)); return S("Unknown CondFlag"); } CondFlag inverse(CondFlag c) { switch (c) { case ifAlways: return ifNever; case ifNever: return ifAlways; case ifOverflow: return ifNoOverflow; case ifNoOverflow: return ifOverflow; case ifEqual: return ifNotEqual; case ifNotEqual: return ifEqual; case ifBelow: return ifAboveEqual; case ifBelowEqual: return ifAbove; case ifAboveEqual: return ifBelow; case ifAbove: return ifBelowEqual; case ifLess: return ifGreaterEqual; case ifLessEqual: return ifGreater; case ifGreaterEqual: return ifLess; case ifGreater: return ifLessEqual; case ifFBelow: return ifFAboveEqual; case ifFBelowEqual: return ifFAbove; case ifFAboveEqual: return ifFBelow; case ifFAbove: return ifFBelowEqual; } TODO(L"Implement!"); assert(false); return ifNever; } } storm-lang-0.5.10/Code/CondFlag.h000066400000000000000000000011121414256633200163610ustar00rootroot00000000000000#pragma once namespace code { STORM_PKG(core.asm); /** * Conditional flags for various op-codes. */ enum CondFlag { ifAlways, ifNever, ifOverflow, ifNoOverflow, ifEqual, ifNotEqual, // Unsigned comparision. ifBelow, ifBelowEqual, ifAboveEqual, ifAbove, // Singned comparision. ifLess, ifLessEqual, ifGreaterEqual, ifGreater, // Float comparision. ifFBelow, ifFBelowEqual, ifFAboveEqual, ifFAbove, }; // Get the string name. const wchar *name(CondFlag cond); // Inverse the flag. CondFlag STORM_FN inverse(CondFlag cond); } storm-lang-0.5.10/Code/Content.cpp000066400000000000000000000011011414256633200166470ustar00rootroot00000000000000#include "stdafx.h" #include "Content.h" #include "RefSource.h" namespace code { Content::Content() {} const void *Content::address() const { return lastAddress; } nat Content::size() const { return lastSize; } void Content::set(const void *addr, nat size) { lastAddress = addr; lastSize = size; if (owner) owner->update(); } Str *Content::ownerName() const { RefSource *o = (RefSource *)atomicRead((void *&)owner); if (o) return o->title(); else return null; } StaticContent::StaticContent(const void *addr) { set(addr, 0); } } storm-lang-0.5.10/Code/Content.h000066400000000000000000000020001414256633200163130ustar00rootroot00000000000000#pragma once #include "Core/TObject.h" namespace code { STORM_PKG(core.asm); class RefSource; /** * Content. Provides a pointer and a size which can be referred to by RefSources, and by * extension, References. Only one RefSource can refer to a Content object. */ class Content : public ObjectOn { STORM_CLASS; public: STORM_CTOR Content(); // Set the address of this content. Don't do this after the content has been added to a // RefSource. void set(const void *address, nat size); // Get last address and size. virtual const void *address() const; virtual nat size() const; // Get the name of the owning RefSource (if any). MAYBE(Str *) STORM_FN ownerName() const; private: friend class RefSource; // Last address and size. UNKNOWN(PTR_GC) const void *lastAddress; nat lastSize; // Owning RefSource. RefSource *owner; }; /** * Static content. */ class StaticContent : public Content { STORM_CLASS; public: StaticContent(const void *ptr); }; } storm-lang-0.5.10/Code/Debug.cpp000066400000000000000000000032541414256633200162760ustar00rootroot00000000000000#include "stdafx.h" #include "Debug.h" // For debugging! #include "Code/X86/Seh.h" #include "Code/X86/SafeSeh.h" namespace code { #if defined(X86) && defined(WINDOWS) NT_TIB *getTIB() { NT_TIB *tib; __asm { // read the "self" member from "fs:0x18" mov eax, fs:0x18; mov tib, eax; } assert(tib == tib->Self); return tib; } struct SehFrame { SehFrame *prev; void *fn; // From the storm code generation. void *block, *part; }; void dumpStack() { void *stackPtr; SehFrame *top; __asm { mov eax, fs:[0]; mov top, eax; mov stackPtr, esp; }; PLN("---- STACK DUMP ----"); NT_TIB *tib = getTIB(); PLN("Stack base: " << tib->StackBase); PLN("Stack top: " << tib->StackLimit); PLN("Stack ptr: " << stackPtr); PLN("Stack size: " << (nat(tib->StackBase) - nat(tib->StackLimit))); if (stackPtr > tib->StackBase) PLN("Above stack."); if (stackPtr < tib->StackLimit) PLN("Below stack."); for (nat i = 0; i < 10; i++) { PLN("SEH at: " << top); nat addr = nat(top); if (addr == null || addr == 0xFFFFFFFF) break; PLN("Handler: " << top->fn << " (storm: " << &x86SafeSEH << ")"); if (nat(top) % 4 != 0) PLN("ERROR: STACK IS NOT WORD-ALIGNED"); if (top->fn == &x86SafeSEH) { void **ebp = (void **)(top + 1); PLN("Ebp: " << ebp); for (nat p = 0; p < 3; p++) PLN("Param " << p << ": " << ebp[2 + p]); for (nat v = 0; v < 5; v++) PLN("Local " << v << ": " << ebp[-5 - v]); } top = top->prev; } PLN("---- DONE ----"); } #else void dumpStack() { PLN("Stack dumping is not implemented for your system yet!"); PLN("See Code/Debug.h for where to implement it."); } #endif } storm-lang-0.5.10/Code/Debug.h000066400000000000000000000003131414256633200157340ustar00rootroot00000000000000#pragma once namespace code { /** * Good utilites for debugging! Most of these are platform dependent. */ // Dump the current stack, with exception handlers and the like. void dumpStack(); } storm-lang-0.5.10/Code/DelegatedRef.cpp000066400000000000000000000007661414256633200175700ustar00rootroot00000000000000#include "stdafx.h" #include "DelegatedRef.h" namespace code { DelegatedContent::DelegatedContent(Ref referTo) { set(referTo); } void DelegatedContent::set(Ref referTo) { ref = new (this) DelegatedRef(this, referTo); set(ref->address(), 0); } Ref DelegatedContent::to() const { return Ref(ref); } DelegatedRef::DelegatedRef(DelegatedContent *owner, Ref to) : Reference(to, owner), owner(owner) {} void DelegatedRef::moved(const void *newAddr) { owner->set(newAddr, 0); } } storm-lang-0.5.10/Code/DelegatedRef.h000066400000000000000000000022771414256633200172340ustar00rootroot00000000000000#pragma once #include "Reference.h" namespace code { STORM_PKG(core.asm); /** * A reference which acts both like a source and a sink. This is useful if one wants to rename a * reference, or create an alias. For example, sometimes it is useful to have two logically * different references to refer to the same underlying function. This is common to make it * possible to introduce vtable lookups when they are needed without paying anything unless they * are needed. */ class DelegatedRef; /** * Create a source which refers to something useful. */ class DelegatedContent : public Content { STORM_CLASS; public: STORM_CTOR DelegatedContent(Ref referTo); // Set to a new reference. void STORM_FN set(Ref ref); using Content::set; // Get the reference we're referring to. Ref STORM_FN to() const; private: // The reference in charge of updating us. DelegatedRef *ref; }; /** * The reference used by DelegatedContent and DelegatedSrc. */ class DelegatedRef : public Reference { STORM_CLASS; public: // Create. STORM_CTOR DelegatedRef(DelegatedContent *owner, Ref to); virtual void moved(const void *newAddr); private: DelegatedContent *owner; }; } storm-lang-0.5.10/Code/Exception.h000066400000000000000000000060141414256633200166500ustar00rootroot00000000000000#pragma once #include "Var.h" #include "Block.h" #include "Core/Exception.h" namespace code { STORM_PKG(core.asm); // Base for all exceptions in the Code backend. class EXCEPTION_EXPORT CodeError : public storm::Exception { STORM_EXCEPTION; public: STORM_CTOR CodeError() { saveTrace(); } }; // Thrown when an instruction contains invalid data for some reason. class EXCEPTION_EXPORT InvalidValue : public CodeError { STORM_EXCEPTION; public: InvalidValue(const wchar *what) { error = new (engine()) Str(what); } STORM_CTOR InvalidValue(Str *what) { error = what; } virtual void STORM_FN message(StrBuf *to) const { *to << S("Invalid value: ") << error; } private: Str *error; }; class EXCEPTION_EXPORT BlockBeginError : public CodeError { STORM_EXCEPTION; public: STORM_CTOR BlockBeginError() { msg = new (engine()) Str(S("The parent scope must be entered before a child scope.")); } STORM_CTOR BlockBeginError(Str *msg) { this->msg = msg; } virtual void STORM_FN message(StrBuf *to) const { *to << msg; } private: Str *msg; }; class EXCEPTION_EXPORT BlockEndError : public CodeError { STORM_EXCEPTION; public: STORM_CTOR BlockEndError() { msg = new (engine()) Str(S("The scope is not the topmost active scope.")); } BlockEndError(Str *msg) { this->msg = msg; } virtual void STORM_FN message(StrBuf *to) const { *to << msg; } private: Str *msg; }; class EXCEPTION_EXPORT FrameError : public CodeError { STORM_EXCEPTION; public: STORM_CTOR FrameError() {} virtual void STORM_FN message(StrBuf *to) const { *to << S("Trying to use an invalid block or variable."); } }; class EXCEPTION_EXPORT DuplicateLabelError : public CodeError { STORM_EXCEPTION; public: STORM_CTOR DuplicateLabelError(Nat id) { this->id = id; } Nat id; virtual void STORM_FN message(StrBuf *to) const { *to << S("Duplicate usage of label ") << id << S("."); } }; class EXCEPTION_EXPORT UnusedLabelError : public CodeError { STORM_EXCEPTION; public: STORM_CTOR UnusedLabelError(Nat id) { this->id = id; } Nat id; virtual void STORM_FN message(StrBuf *to) const { *to << S("Use of undefined label ") << id << S("."); } }; class EXCEPTION_EXPORT VariableUseError : public CodeError { STORM_EXCEPTION; public: STORM_CTOR VariableUseError(Var v, Block b) { var = v; block = b; } Var var; Block block; virtual void STORM_FN message(StrBuf *to) const { *to << S("Trying to use ") << var << S(" in ") << block << S(", where it is not accessible."); } }; class EXCEPTION_EXPORT VariableActivationError : public CodeError { STORM_EXCEPTION; public: VariableActivationError(Var v, const wchar *msg) { this->var = v; this->msg = new (this) Str(msg); } STORM_CTOR VariableActivationError(Var v, Str *msg) { this->var = v; this->msg = msg; } Var var; Str *msg; virtual void STORM_FN message(StrBuf *to) const { *to << S("Error activating the variable ") << var << S(": ") << msg; } }; } storm-lang-0.5.10/Code/Instr.cpp000066400000000000000000000325241414256633200163510ustar00rootroot00000000000000#include "stdafx.h" #include "Instr.h" #include "Exception.h" #include "Reg.h" #include "Core/StrBuf.h" #include "Core/CloneEnv.h" namespace code { Instr::Instr() : iOp(op::nop) {} Instr::Instr(const Instr &o) : iOp(o.iOp), iSrc(o.iSrc), iDest(o.iDest) {} Instr::Instr(op::OpCode op, const Operand &dest, const Operand &src) : iOp(op), iDest(dest), iSrc(src) {} void Instr::deepCopy(CloneEnv *env) { // Everything is constant here anyway. No need! } Size Instr::size() const { return max(iSrc.size(), iDest.size()); } op::OpCode Instr::op() const { return iOp; } DestMode Instr::mode() const { return destMode(iOp); } Operand Instr::src() const { return iSrc; } Operand Instr::dest() const { return iDest; } Instr *Instr::alter(Operand dest, Operand src) { return new (this) Instr(iOp, dest, src); } Instr *Instr::alterSrc(Operand src) { return new (this) Instr(iOp, iDest, src); } Instr *Instr::alterDest(Operand dest) { return new (this) Instr(iOp, dest, iSrc); } void Instr::toS(StrBuf *to) const { *to << code::name(iOp); if (iSrc.empty() && iDest.empty()) { // No operands. } else if (iDest.empty()) { *to << L" " << iSrc; } else if (iSrc.empty()) { *to << L" " << iDest; } else { *to << L" " << iDest << L", " << iSrc; } } Instr *instr(EnginePtr e, op::OpCode op) { return new (e.v) Instr(op, Operand(), Operand()); } Instr *instrSrc(EnginePtr e, op::OpCode op, Operand src) { src.ensureReadable(op); return new (e.v) Instr(op, Operand(), src); } Instr *instrDest(EnginePtr e, op::OpCode op, Operand dest) { DestMode mode = destMode(op); if (mode == destNone) throw new (e.v) InvalidValue(S("Can not pass 'destNone' to 'instrDest'.")); if (mode & destRead) dest.ensureReadable(op); if (mode & destWrite) dest.ensureWritable(op); return new (e.v) Instr(op, dest, Operand()); } Instr *instrDestSrc(EnginePtr e, op::OpCode op, Operand dest, Operand src) { DestMode mode = destMode(op); if (mode == destNone) throw new (e.v) InvalidValue(S("Can not pass 'destNone' to 'instrDestSrc'.")); if (mode & destRead) dest.ensureReadable(op); if (mode & destWrite) dest.ensureWritable(op); src.ensureReadable(op); if (dest.size() != src.size()) { Str *msg = TO_S(e.v, S("For ") << name(op) << S(": Size of operands must match! ") << dest << S(" vs ") << src); throw new (e.v) InvalidValue(msg); } return new (e.v) Instr(op, dest, src); } Instr *instrLoose(EnginePtr e, op::OpCode op, Operand dest, Operand src) { return new (e.v) Instr(op, dest, src); } /** * Additional information, used with function calls. */ TypeInstr::TypeInstr(op::OpCode opCode, const Operand &dest, const Operand &src, TypeDesc *type, Bool member) : Instr(opCode, dest, src), type(type), member(member) {} void TypeInstr::deepCopy(CloneEnv *env) { Instr::deepCopy(env); cloned(type, env); } void TypeInstr::toS(StrBuf *to) const { Instr::toS(to); *to << S(" - ") << type; if (member) *to << S("[member]"); } Instr *TypeInstr::alter(Operand dest, Operand src) { return new (this) TypeInstr(iOp, dest, src, type, member); } Instr *TypeInstr::alterSrc(Operand src) { return new (this) TypeInstr(iOp, iDest, src, type, member); } Instr *TypeInstr::alterDest(Operand dest) { return new (this) TypeInstr(iOp, dest, iSrc, type, member); } /** * Instructions. */ static Operand sizedReg(Engine &e, Reg base, Size size) { Reg s = asSize(base, size); if (s == noReg) { if (size == Size()) return Operand(); else throw new (e) InvalidValue(S("The return size must fit in a register (ie. < 8 bytes).")); } else { return Operand(s); } } Instr *mov(EnginePtr e, Operand to, Operand from) { return instrDestSrc(e, op::mov, to, from); } Instr *swap(EnginePtr e, Reg a, Operand b) { b.ensureWritable(op::swap); return instrDestSrc(e, op::swap, a, b); } Instr *lea(EnginePtr e, Operand to, Operand from) { if (to.size() != Size::sPtr) throw new (e.v) InvalidValue(S("Lea must update a pointer.")); switch (from.type()) { case opRelative: case opVariable: case opReference: // These are ok. break; default: { Str *msg = TO_S(e.v, S("lea must be used with a complex addressing mode or a reference.") S(" (Got ") << from << S(")")); throw new (e.v) InvalidValue(msg); } } return instrLoose(e, op::lea, to, from); } Instr *push(EnginePtr e, Operand v) { return instrSrc(e, op::push, v); } Instr *pop(EnginePtr e, Operand to) { return instrDest(e, op::pop, to); } Instr *pushFlags(EnginePtr e) { return instr(e, op::pushFlags); } Instr *popFlags(EnginePtr e) { return instr(e, op::popFlags); } Instr *jmp(EnginePtr e, Operand to) { if (to.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must jump to a pointer, trying to jump to ") << to)); return instrLoose(e, op::jmp, to, CondFlag(ifAlways)); } Instr *jmp(EnginePtr e, Label to, CondFlag cond) { return instrLoose(e, op::jmp, to, cond); } Instr *setCond(EnginePtr e, Operand to, CondFlag cond) { if (to.size() != Size::sByte) throw new (e.v) InvalidValue(S("Must set a byte.")); return instrLoose(e, op::setCond, to, cond); } Instr *call(EnginePtr e, Operand to, Size ret) { if (to.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must call a pointer, tried calling ") << to)); return instrLoose(e, op::call, sizedReg(e.v, ptrA, ret), to); } Instr *ret(EnginePtr e, Size ret) { Operand r = sizedReg(e.v, ptrA, ret); if (r.type() == opNone) return instr(e, op::ret); else return instrSrc(e, op::ret, r); } Instr *fnParam(EnginePtr e, TypeDesc *type, Operand src) { if (src.size() != type->size()) { Str *msg = TO_S(e.v, S("Size mismatch for 'fnParam'. Got ") << src.size() << S(", expected ") << type->size()); throw new (e.v) InvalidValue(msg); } return new (e.v) TypeInstr(op::fnParam, Operand(), src, type, false); } Instr *fnParamRef(EnginePtr e, TypeDesc *type, Operand src) { if (src.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must use a pointer with 'fnParamRef'. Used ") << src)); return new (e.v) TypeInstr(op::fnParamRef, Operand(), src, type, false); } Instr *fnCall(EnginePtr e, Operand call, Bool member) { if (call.type() == opConstant) throw new (e.v) InvalidValue(S("Should not call constant values, use references instead!")); if (call.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must call a pointer, tried calling ") << call)); return new (e.v) TypeInstr(op::fnCall, Operand(), call, voidDesc(e), member); } Instr *fnCall(EnginePtr e, Operand call, Bool member, TypeDesc *result, Operand to) { if (call.type() == opConstant) throw new (e.v) InvalidValue(S("Should not call constant values, use references instead!")); if (call.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must call a pointer, tried calling ") << call)); if (to.size() != result->size()) { Str *msg = TO_S(e.v, S("Size mismatch for 'fnCall'. Got ") << to.size() << S(", expected ") << result->size()); throw new (e.v) InvalidValue(msg); } return new (e.v) TypeInstr(op::fnCall, to, call, result, member); } Instr *fnCallRef(EnginePtr e, Operand call, Bool member, TypeDesc *result, Operand to) { if (call.type() == opConstant) throw new (e.v) InvalidValue(S("Should not call constant values, use references instead!")); if (call.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must call a pointer, tried calling ") << call)); if (to.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must use a pointer with 'fnCallRef'. Used ") << to)); return new (e.v) TypeInstr(op::fnCallRef, to, call, result, member); } Instr *fnRet(EnginePtr e, Operand src) { return instrSrc(e, op::fnRet, src); } Instr *fnRetRef(EnginePtr e, Operand src) { if (src.size() != Size::sPtr) throw new (e.v) InvalidValue(TO_S(e.v, S("Must use a pointer with 'fnRetRef'. Used ") << src)); return instrSrc(e, op::fnRetRef, src); } Instr *fnRet(EnginePtr e) { return instr(e, op::fnRet); } Instr *bor(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::bor, dest, src); } Instr *band(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::band, dest, src); } Instr *bxor(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::bxor, dest, src); } Instr *bnot(EnginePtr e, Operand dest) { return instrDest(e, op::bnot, dest); } Instr *add(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::add, dest, src); } Instr *adc(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::adc, dest, src); } Instr *sub(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::sub, dest, src); } Instr *sbb(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::sbb, dest, src); } Instr *cmp(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::cmp, dest, src); } Instr *mul(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::mul, dest, src); } Instr *idiv(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::idiv, dest, src); } Instr *udiv(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::udiv, dest, src); } Instr *imod(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::imod, dest, src); } Instr *umod(EnginePtr e, Operand dest, Operand src) { return instrDestSrc(e, op::umod, dest, src); } Instr *shl(EnginePtr e, Operand dest, Operand src) { if (src.size() != Size::sByte) throw new (e.v) InvalidValue(S("Size for shl must be 1")); return instrLoose(e, op::shl, dest, src); } Instr *shr(EnginePtr e, Operand dest, Operand src) { if (src.size() != Size::sByte) throw new (e.v) InvalidValue(S("Size for shr must be 1")); return instrLoose(e, op::shr, dest, src); } Instr *sar(EnginePtr e, Operand dest, Operand src) { if (src.size() != Size::sByte) throw new (e.v) InvalidValue(S("Size for sar must be 1")); return instrLoose(e, op::sar, dest, src); } Instr *icast(EnginePtr e, Operand dest, Operand src) { return instrLoose(e, op::icast, dest, src); } Instr *ucast(EnginePtr e, Operand dest, Operand src) { return instrLoose(e, op::ucast, dest, src); } Instr *fstp(EnginePtr e, Operand dest) { if (dest.type() == opRegister) throw new (e.v) InvalidValue(S("Can not store to register.")); if (dest.size() != Size::sFloat && dest.size() != Size::sDouble) throw new (e.v) InvalidValue(S("Invalid size.")); return instrDest(e, op::fstp, dest); } Instr *fistp(EnginePtr e, Operand dest) { if (dest.size() != Size::sInt && dest.size() != Size::sLong) throw new (e.v) InvalidValue(S("Invalid size.")); return instrDest(e, op::fistp, dest); } Instr *fld(EnginePtr e, Operand src) { if (src.type() == opRegister) throw new (e.v) InvalidValue(S("Can not load a float from register.")); if (src.type() == opConstant) throw new (e.v) InvalidValue(S("Can not load a float from a constant.")); if (src.size() != Size::sFloat && src.size() != Size::sDouble) throw new (e.v) InvalidValue(S("Invalid size.")); return instrSrc(e, op::fld, src); } Instr *fild(EnginePtr e, Operand src) { if (src.type() == opRegister) throw new (e.v) InvalidValue(S("Can not load from register.")); if (src.type() == opConstant) throw new (e.v) InvalidValue(S("Can not load from a constant.")); if (src.size() != Size::sInt && src.size() != Size::sLong) throw new (e.v) InvalidValue(S("Invalid size.")); return instrSrc(e, op::fild, src); } Instr *fldz(EnginePtr e) { return instr(e, op::fldz); } Instr *faddp(EnginePtr e) { return instr(e, op::faddp); } Instr *fsubp(EnginePtr e) { return instr(e, op::fsubp); } Instr *fmulp(EnginePtr e) { return instr(e, op::fmulp); } Instr *fdivp(EnginePtr e) { return instr(e, op::fdivp); } Instr *fcompp(EnginePtr e) { return instr(e, op::fcompp); } Instr *fwait(EnginePtr e) { return instr(e, op::fwait); } Instr *dat(EnginePtr e, Operand v) { switch (v.type()) { case opConstant: case opLabel: case opReference: case opObjReference: break; default: throw new (e.v) InvalidValue(S("Cannot store other than references, constants and labels in dat")); } return instrSrc(e, op::dat, v); } Instr *lblOffset(EnginePtr e, Label l) { return instrSrc(e, op::lblOffset, l); } Instr *align(EnginePtr e, Offset o) { return instrSrc(e, op::align, ptrConst(o)); } Instr *alignAs(EnginePtr e, Size a) { return align(e, Offset(a.align32(), a.align64())); } Instr *prolog(EnginePtr e) { return instr(e, op::prolog); } Instr *epilog(EnginePtr e) { return instr(e, op::epilog); } Instr *preserve(EnginePtr e, Operand dest, Reg reg) { return instrDestSrc(e, op::preserve, dest, reg); } Instr *location(EnginePtr e, SrcPos pos) { return instrLoose(e, op::location, Operand(), pos); } Instr *begin(EnginePtr e, Block block) { return instrLoose(e, op::beginBlock, Operand(), block); } Instr *end(EnginePtr e, Block block) { return instrLoose(e, op::endBlock, Operand(), block); } Instr *jmpBlock(EnginePtr e, Label to, Block block) { return instrLoose(e, op::jmpBlock, to, block); } Instr *activate(EnginePtr e, Var var) { return instrLoose(e, op::activate, Operand(), var); } Instr *threadLocal(EnginePtr e) { return instr(e, op::threadLocal); } } storm-lang-0.5.10/Code/Instr.h000066400000000000000000000234531414256633200160170ustar00rootroot00000000000000#pragma once #include "OpCode.h" #include "Operand.h" #include "CondFlag.h" #include "TypeDesc.h" #include "Core/Object.h" #include "Core/EnginePtr.h" #include "Utils/Bitmask.h" namespace code { STORM_PKG(core.asm); /** * Class representing an entire asm-instruction. * * Note: immutable class. */ class Instr : public Object { STORM_CLASS; public: // Create a no-op instruction. STORM_CTOR Instr(); // Copy this instruction. Instr(const Instr &o); // Deep copy. virtual void STORM_FN deepCopy(CloneEnv *env); // Access to the members: op::OpCode STORM_FN op() const; Operand STORM_FN src() const; Operand STORM_FN dest() const; DestMode STORM_FN mode() const; // Get the maximum size of the operands. Size STORM_FN size() const; // Create another instruction based off this one. Intended to be used by backends -> no sanity checking. virtual Instr *STORM_FN alter(Operand dest, Operand src); virtual Instr *STORM_FN alterSrc(Operand src); virtual Instr *STORM_FN alterDest(Operand dest); // To string. virtual void STORM_FN toS(StrBuf *to) const; protected: // Private constructor, used by 'instrXxx()' functions below. Instr(op::OpCode opCode, const Operand &dest, const Operand &src); // Op-code. op::OpCode iOp; // Src and dest fields. Operand iSrc; Operand iDest; // Creators. friend Instr *instr(EnginePtr e, op::OpCode op); friend Instr *instrSrc(EnginePtr e, op::OpCode op, Operand src); friend Instr *instrDest(EnginePtr e, op::OpCode op, Operand dest); friend Instr *instrDestSrc(EnginePtr e, op::OpCode op, Operand dest, Operand src); friend Instr *instrLoose(EnginePtr e, op::OpCode op, Operand dest, Operand src); }; /** * Additional information required for FnCall and FnRet operations. Contains an additional * 'TypeDesc' describing the value being used in detail. * * TODO: If we wanted to, we could probably squeeze the extra pointer inside the Operand in a * regular Instr somehow. I do not, however, think the effort is worth it. */ class TypeInstr : public Instr { STORM_CLASS; public: // Create. TypeInstr(op::OpCode opCode, const Operand &dest, const Operand &src, TypeDesc *type, Bool member); // The type of used in this instruction. TypeDesc *type; // Is this a call to a member function? Only used for 'fnCall' instructions. Bool member; // Alter various parts. virtual Instr *STORM_FN alter(Operand dest, Operand src); virtual Instr *STORM_FN alterSrc(Operand src); virtual Instr *STORM_FN alterDest(Operand dest); // Deep copy. virtual void STORM_FN deepCopy(CloneEnv *env); // To string. virtual void STORM_FN toS(StrBuf *to) const; }; // Create an instruction without operands. Instr *STORM_FN instr(EnginePtr e, op::OpCode op); Instr *STORM_FN instrSrc(EnginePtr e, op::OpCode op, Operand src); Instr *STORM_FN instrDest(EnginePtr e, op::OpCode op, Operand dest); Instr *STORM_FN instrDestSrc(EnginePtr e, op::OpCode op, Operand dest, Operand src); // Create without checking the sanity of the parameters. Used for pseudo-instructions and predicates among others. Instr *STORM_FN instrLoose(EnginePtr e, op::OpCode op, Operand dest, Operand src); /** * Create instructions: */ Instr *STORM_FN mov(EnginePtr e, Operand to, Operand from); Instr *STORM_FN swap(EnginePtr e, Reg a, Operand b); Instr *STORM_FN jmp(EnginePtr e, Operand to); Instr *STORM_FN jmp(EnginePtr e, Label to, CondFlag cond); Instr *STORM_FN call(EnginePtr e, Operand to, Size resultSize); Instr *STORM_FN ret(EnginePtr e, Size resultSize); // Returns whatever is in eax register. // Note: Be very careful when using push and pop in code that performs function calls! Certain // architectures require the stack to be properly aligned when calling a function. The different // backends takes care of this for you, but they will not keep track of push and pop // instructions so that they can compensate for any additional instructions used. Instr *STORM_FN push(EnginePtr e, Operand v); Instr *STORM_FN pop(EnginePtr e, Operand to); Instr *STORM_FN pushFlags(EnginePtr e); Instr *STORM_FN popFlags(EnginePtr e); // This one has somewhat special semantics, when used with a reference as 'from', it instead // loads the RefSource referred to by the Ref. Instr *STORM_FN lea(EnginePtr e, Operand to, Operand from); // Set a byte to the result of an operation. Instr *STORM_FN setCond(EnginePtr e, Operand to, CondFlag cond); // Function calls. Works like this: fnParam(10), fnParam(20), fnCall(myFn). Labels placed between fnParam // and fnCall will effectively be before the first fnParam. All fnParams will be merged into the fnCall, so do // not place other instructions between. Parameters are entered "right to left". Ie, the above example // calls myFn(10, 20). // The variations ending in 'Ref' assumes that the source (or destination) is a pointer rather than // the target itself. Instr *STORM_FN fnParam(EnginePtr e, TypeDesc *type, Operand src); Instr *STORM_FN fnParamRef(EnginePtr e, TypeDesc *type, Operand src); Instr *STORM_FN fnCall(EnginePtr e, Operand call, Bool member); // no result Instr *STORM_FN fnCall(EnginePtr e, Operand call, Bool member, TypeDesc *result, Operand to); Instr *STORM_FN fnCallRef(EnginePtr e, Operand call, Bool member, TypeDesc *result, Operand to); // High-level function return. Examines the return type specified in the listing and handles the // return properly according to the current platform. Implies an 'epilog' instruction as well. Instr *STORM_FN fnRet(EnginePtr e, Operand src); Instr *STORM_FN fnRetRef(EnginePtr e, Operand src); Instr *STORM_FN fnRet(EnginePtr e); // Integer math (signed/unsigned) Instr *STORM_FN STORM_NAME(bor, or)(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN STORM_NAME(band, and)(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN STORM_NAME(bxor, xor)(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN STORM_NAME(bnot, not)(EnginePtr e, Operand dest); Instr *STORM_FN add(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN adc(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN sub(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN sbb(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN cmp(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN mul(EnginePtr e, Operand dest, Operand src); // Signed math Instr *STORM_FN idiv(EnginePtr e, Operand dest, Operand src); // div a, b <=> a = a / b Instr *STORM_FN imod(EnginePtr e, Operand dest, Operand src); // Unsigned math Instr *STORM_FN udiv(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN umod(EnginePtr e, Operand dest, Operand src); // Shifts. src.size() == 1 Instr *STORM_FN shl(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN shr(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN sar(EnginePtr e, Operand dest, Operand src); // Resize operands. Works like a mov, but sign-extends if needed. Instr *STORM_FN icast(EnginePtr e, Operand dest, Operand src); Instr *STORM_FN ucast(EnginePtr e, Operand dest, Operand src); // Floating point math. Instr *STORM_FN fstp(EnginePtr e, Operand dest); Instr *STORM_FN fistp(EnginePtr e, Operand dest); // Truncates results. Instr *STORM_FN fld(EnginePtr e, Operand src); Instr *STORM_FN fild(EnginePtr e, Operand src); Instr *STORM_FN fldz(EnginePtr e); Instr *STORM_FN faddp(EnginePtr e); Instr *STORM_FN fsubp(EnginePtr e); Instr *STORM_FN fmulp(EnginePtr e); Instr *STORM_FN fdivp(EnginePtr e); Instr *STORM_FN fcompp(EnginePtr e); Instr *STORM_FN fwait(EnginePtr e); // Data. Instr *STORM_FN dat(EnginePtr e, Operand v); // Offset of a label. Instr *STORM_FN lblOffset(EnginePtr e, Label l); // Align to specific size. Instr *STORM_FN alignAs(EnginePtr e, Size v); Instr *STORM_FN align(EnginePtr e, Offset v); // Function prolog/epilog. The prolog will automatically initialize the root block, and the epilog // will automatically destroy all active blocks. The epilog does not count as closing a block, and // may therefore be used inside functions to implement a return statement. For example: // listing << prolog() << begin(block2) << epilog() << ret() << ... << end(block2) << ... << epilog() << ret(); // Here: block2 is considered to continue all the way between begin() and end(), even though the block is // also ended within the epilog in the middle. The epilog only preserves ptrA register. Instr *STORM_FN prolog(EnginePtr e); Instr *STORM_FN epilog(EnginePtr e); // Note that a register has been preserved on the stack. Instr *STORM_FN preserve(EnginePtr e, Operand dest, Reg reg); // Node a source position. Instr *STORM_FN location(EnginePtr e, SrcPos pos); // Scope management. A scope is assumed to be open on all instructions between "begin(scope)" and "end(scope)". // Do not, for example, have all begin/end scopes in the end of the listing and jump forth and back between them. // That makes the exception handling fail to detect what to destroy. NOTE: Does not preserve registers! Instr *STORM_FN begin(EnginePtr e, Block block); Instr *STORM_FN end(EnginePtr e, Block block); // End zero or more blocks and then jump to 'label'. Ends block until the current topmost block // is 'block'. As is the case with 'epilog', this does not affect the current block at the // location just after the 'endJmp' instruction. As such, this pseudo-op is useful for early // block exits, such as 'continue' and 'break' statements. Instr *STORM_FN jmpBlock(EnginePtr e, Label to, Block block); // Activate destruction of a particular variable from here on in the listing. Instr *STORM_FN activate(EnginePtr e, Var var); // Segment override for the next instruction, not reliable for function calls or double memory accesses. Only // availiable on relevant architectures, for example X86. Instr *STORM_FN threadLocal(EnginePtr e); // Note: add a forwarder in InstrFwd as well! } storm-lang-0.5.10/Code/InstrFwd.h000066400000000000000000000122311414256633200164500ustar00rootroot00000000000000#pragma once namespace code { /** * Make these functions more convenient to call in C++ during certain conditions: * * It inhibits the clarity and it is a bit annoying to have to do: * *foo << mov(e, eax, ebx); * * instead of: * *foo << mov(eax, ebx); * * The first engine parameter is not visible in Storm (as it will insert that for us * automatically), but in C++ we need another mechanism as C++ does not know which engine we * will use. So in the context of directly appending instructions to a Listing object, we can * save the parameters in some container temporarily and delay the call to the actual creating * function until the result is appended to a Listing. When this happens, we can get an Engine * from there and create the Instr object properly. This is done with the template magic * below. Sadly, we need to declare all machine operations once more at the end, but that is a * small price to pay. */ template class InstrProxy0 {}; template inline Listing &operator <<(Listing &l, const InstrProxy0 &p) { return l << (*Fn)(l.engine()); } template class InstrProxy1 { public: const T &t; InstrProxy1(const T &t) : t(t) {} }; template inline Listing &operator <<(Listing &l, const InstrProxy1 &p) { return l << (*Fn)(l.engine(), p.t); } template class InstrProxy2 { public: const T &t; const U &u; InstrProxy2(const T &t, const U &u) : t(t), u(u) {} }; template inline Listing &operator <<(Listing &l, const InstrProxy2 &p) { return l << (*Fn)(l.engine(), p.t, p.u); } template class InstrProxy3 { public: const T &t; const U &u; const V &v; InstrProxy3(const T &t, const U &u, const V &v) : t(t), u(u), v(v) {} }; template inline Listing &operator <<(Listing &l, const InstrProxy3 &p) { return l << (*Fn)(l.engine(), p.t, p.u, p.v); } template class InstrProxy4 { public: const T &t; const U &u; const V &v; const W &w; InstrProxy4(const T &t, const U &u, const V &v, const W &w) : t(t), u(u), v(v), w(w) {} }; template inline Listing &operator <<(Listing &l, const InstrProxy4 &p) { return l << (*Fn)(l.engine(), p.t, p.u, p.v, p.w); } #define PROXY0(op) \ inline InstrProxy0<&op> op() { \ return InstrProxy0<&op>(); \ } #define PROXY1(op, T) \ inline InstrProxy1 op(T const &t) { \ return InstrProxy1(t); \ } #define PROXY2(op, T, U) \ inline InstrProxy2 op(T const &t, U const &u) { \ return InstrProxy2(t, u); \ } #define PROXY3(op, T, U, V) \ inline InstrProxy3 op(T const &t, U const &u, V const &v) { \ return InstrProxy3(t, u, v); \ } #define PROXY4(op, T, U, V, W) \ inline InstrProxy4 op(T const &t, U const &u, V const &v, W const &w) { \ return InstrProxy4(t, u, v, w); \ } // Repetition of all OP-codes. PROXY2(mov, Operand, Operand); PROXY2(swap, Reg, Operand); PROXY1(push, Operand); PROXY1(pop, Operand); PROXY0(pushFlags); PROXY0(popFlags); PROXY1(jmp, Operand); PROXY2(jmp, Label, CondFlag); PROXY2(call, Operand, Size); PROXY1(ret, Size); PROXY2(lea, Operand, Operand); PROXY2(setCond, Operand, CondFlag); PROXY2(fnParam, TypeDesc *, Operand); PROXY2(fnParamRef, TypeDesc *, Operand); PROXY2(fnCall, Operand, Bool); PROXY4(fnCall, Operand, Bool, TypeDesc *, Operand); PROXY4(fnCallRef, Operand, Bool, TypeDesc *, Operand); PROXY1(fnRet, Operand); PROXY1(fnRetRef, Operand); PROXY0(fnRet); PROXY2(bor, Operand, Operand); PROXY2(band, Operand, Operand); PROXY2(bxor, Operand, Operand); PROXY1(bnot, Operand); PROXY2(add, Operand, Operand); PROXY2(adc, Operand, Operand); PROXY2(sub, Operand, Operand); PROXY2(sbb, Operand, Operand); PROXY2(cmp, Operand, Operand); PROXY2(mul, Operand, Operand); PROXY2(idiv, Operand, Operand); PROXY2(imod, Operand, Operand); PROXY2(udiv, Operand, Operand); PROXY2(umod, Operand, Operand); PROXY2(shl, Operand, Operand); PROXY2(shr, Operand, Operand); PROXY2(sar, Operand, Operand); PROXY2(icast, Operand, Operand); PROXY2(ucast, Operand, Operand); PROXY1(fstp, Operand); PROXY1(fistp, Operand); PROXY1(fld, Operand); PROXY1(fild, Operand); PROXY0(fldz); PROXY0(faddp); PROXY0(fsubp); PROXY0(fmulp); PROXY0(fdivp); PROXY0(fcompp); PROXY0(fwait); PROXY1(dat, Operand); PROXY1(lblOffset, Label); PROXY1(align, Offset); PROXY1(alignAs, Size); PROXY0(prolog); PROXY0(epilog); PROXY2(preserve, Operand, Reg); PROXY1(location, SrcPos); PROXY1(begin, Block); PROXY1(end, Block); PROXY2(jmpBlock, Label, Block); PROXY1(activate, Var); PROXY0(threadLocal); } storm-lang-0.5.10/Code/Label.cpp000066400000000000000000000005111414256633200162600ustar00rootroot00000000000000#include "stdafx.h" #include "Label.h" #include "Core/StrBuf.h" namespace code { Label::Label() : id(-1) {} Label::Label(Nat id) : id(id) {} void Label::deepCopy(CloneEnv *) {} wostream &operator <<(wostream &to, Label l) { return to << l.id; } StrBuf &operator <<(StrBuf &to, Label l) { return to << l.id; } } storm-lang-0.5.10/Code/Label.h000066400000000000000000000013451414256633200157330ustar00rootroot00000000000000#pragma once namespace code { STORM_PKG(core.asm); /** * A label inside a listing. */ class Label { STORM_VALUE; public: STORM_CTOR Label(); inline Bool STORM_FN operator ==(Label o) const { return id == o.id; } inline Bool STORM_FN operator !=(Label o) const { return id != o.id; } inline Nat STORM_FN key() const { return id; } void STORM_FN deepCopy(CloneEnv *env); private: friend class Listing; friend class Output; friend class Operand; friend class Binary; friend wostream &operator <<(wostream &to, Label l); friend StrBuf &operator <<(StrBuf &to, Label l); explicit Label(Nat id); Nat id; }; wostream &operator <<(wostream &to, Label l); StrBuf &operator <<(StrBuf &to, Label l); } storm-lang-0.5.10/Code/Layout.cpp000066400000000000000000000023471414256633200165270ustar00rootroot00000000000000#include "stdafx.h" #include "Layout.h" namespace code { static Offset addVar(Listing *src, Array *db, Array *valid, Var v) { // Parameter? if (src->isParam(v)) return Offset(); // Invalid? if (v == Var()) return Offset(); Nat id = v.key(); // We've got something useful! if (valid->at(id)) return db->at(id); Var prevVar = src->prev(v); Offset offset = addVar(src, db, valid, prevVar); if (!src->isParam(prevVar)) offset += prevVar.size().aligned(); // Align 'prev' to something useful. offset = offset.alignAs(v.size()); valid->at(id) = true; db->at(id) = offset; return offset; } Array *layout(Listing *src) { Array *all = src->allVars(); Array *result = new (src) Array(all->count() + 1, Offset()); Array *populated = new (src) Array(all->count(), false); Offset worst; for (nat i = 0; i < all->count(); i++) { Offset o = addVar(src, result, populated, all->at(i)); o = (o + all->at(i).size()); // Align to the most restrictive of pointer alignment and the alignment of the type. o = o.alignAs(Size::sPtr).alignAs(all->at(i).size()); worst = max(worst, o); } result->last() = worst; return result; } } storm-lang-0.5.10/Code/Layout.h000066400000000000000000000007171414256633200161730ustar00rootroot00000000000000#pragma once #include "Core/Object.h" #include "Core/Array.h" #include "Listing.h" #include "Size.h" namespace code { STORM_PKG(core.asm); /** * Compute the layout of all local variables inside a listing. Does not even attempt to generate * offsets for parameters. Those entries are left as Offset() (ie. zero). * * The last entry in the returned array is the size of the stack, aligned to sPtr. */ Array *STORM_FN layout(Listing *src); } storm-lang-0.5.10/Code/Listing.cpp000066400000000000000000000361271414256633200166660ustar00rootroot00000000000000#include "stdafx.h" #include "Listing.h" #include "Exception.h" #include "UsedRegs.h" #include "Arena.h" #include "Core/Str.h" #include "Core/StrBuf.h" #include "Core/CloneEnv.h" namespace code { static const nat invalid = -1; wostream &operator <<(wostream &to, FreeOpt o) { if ((o & freeOnBoth) == freeOnBoth) { to << L"always"; } else if (o & freeOnBlockExit) { to << L"on block exit"; } else if (o & freeOnException) { to << L"on exception"; } else { to << L"never"; if (o & freeInactive) to << L", needs activation"; return to; } if (o & freePtr) to << L", by ptr"; else to << L", by value"; if (o & freeInactive) to << L", needs activation"; return to; } StrBuf &operator <<(StrBuf &to, FreeOpt o) { if ((o & freeOnBoth) == freeOnBoth) { to << S("always"); } else if (o & freeOnBlockExit) { to << S("on block exit"); } else if (o & freeOnException) { to << S("on exception"); } else { to << S("never"); if (o & freeInactive) to << S(", needs activation"); return to; } if (o & freePtr) to << S(", by ptr"); else to << S(", by value"); if (o & freeInactive) to << S(", needs activation"); return to; } Listing::Entry::Entry() : instr(null), labels(null) {} Listing::Entry::Entry(Instr *i) : instr(i), labels(null) {} Listing::Entry::Entry(Instr *i, Array