[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/Xsankor/daScript/master/src/ast/ast_simulate.cpp [Back]  [Original]

#include "daScript/misc/platform.h"

#include "daScript/ast/ast.h"
#include "daScript/ast/ast_match.h"
#include "daScript/ast/ast_expressions.h"

#include "daScript/simulate/runtime_array.h"
#include "daScript/simulate/runtime_table_nodes.h"
#include "daScript/simulate/runtime_range.h"
#include "daScript/simulate/runtime_string_delete.h"
#include "daScript/simulate/hash.h"

#include "daScript/simulate/simulate_nodes.h"

#include "daScript/simulate/simulate_visit_op.h"

namespace das
{
    // common for move and copy

    SimNode * makeLocalCMResMove (const LineInfo & at, Context & context, uint32_t offset, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, offset);
                return right;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, offset);
                return right;
            }
        }
        // now, to the regular move
        auto left = context.code->makeNode(at, offset);
        auto right = rE->simulate(context);
        if ( rightType.isRef() ) {
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else {
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * makeLocalCMResCopy(const LineInfo & at, Context & context, uint32_t offset, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        DAS_ASSERT ( rightType.canCopy() &&
                "we are calling makeLocalCMResCopy on a type, which can't be copied."
                "we should not be here, script compiler should have caught this during compilation."
                "compiler later will likely report internal compilation error.");
        auto right = rE->simulate(context);
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * rightC = (SimNode_CallBase *) right;
                rightC->cmresEval = context.code->makeNode(rE->at, offset);
                return rightC;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * rightC = (SimNode_CallBase *) right;
                rightC->cmresEval = context.code->makeNode(rE->at, offset);
                return rightC;
            }
        }
        // wo standard path
        auto left = context.code->makeNode(rE->at, offset);
        if ( rightType.isHandle() ) {
            auto resN = rightType.annotation->simulateCopy(context, at, left,
                (!rightType.isRefType() && rightType.ref) ? rightType.annotation->simulateRef2Value(context, at, right) : right);
            if ( !resN ) {
                context.thisProgram->error("integration error, simulateCopy returned null", "", "",
                                           at, CompilationError::missing_node );
            }
            return resN;
        } else if ( rightType.isRef() ) {
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else {
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * makeLocalRefMove (const LineInfo & at, Context & context, uint32_t stackTop, uint32_t offset, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, stackTop, offset);
                return right;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, stackTop, offset);
                return right;
            }
        }
        // now, to the regular move
        auto left = context.code->makeNode(at, stackTop, offset);
        auto right = rE->simulate(context);
        if ( rightType.isRef() ) {
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else {
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * makeLocalRefCopy(const LineInfo & at, Context & context, uint32_t stackTop, uint32_t offset, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        DAS_ASSERT ( rightType.canCopy() &&
                "we are calling makeLocalRefCopy on a type, which can't be copied."
                "we should not be here, script compiler should have caught this during compilation."
                "compiler later will likely report internal compilation error.");
        auto right = rE->simulate(context);
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * rightC = (SimNode_CallBase *) right;
                rightC->cmresEval = context.code->makeNode(rE->at, stackTop, offset);
                return rightC;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * rightC = (SimNode_CallBase *) right;
                rightC->cmresEval = context.code->makeNode(rE->at, stackTop, offset);
                return rightC;
            }
        }
        // wo standard path
        auto left = context.code->makeNode(rE->at, stackTop, offset);
        if ( rightType.isHandle() ) {
            auto resN = rightType.annotation->simulateCopy(context, at, left,
                (!rightType.isRefType() && rightType.ref) ? rightType.annotation->simulateRef2Value(context, at, right) : right);
            if ( !resN ) {
                context.thisProgram->error("integration error, simulateCopy returned null", "", "",
                                           at, CompilationError::missing_node );
            }
            return resN;
        } else if ( rightType.isRef() ) {
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else {
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * makeLocalMove (const LineInfo & at, Context & context, uint32_t stackTop, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, stackTop);
                return right;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, stackTop);
                return right;
            }
        }
        // now, to the regular move
        auto left = context.code->makeNode(at, stackTop);
        auto right = rE->simulate(context);
        if ( rightType.isRef() ) {
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else  {
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * makeLocalCopy(const LineInfo & at, Context & context, uint32_t stackTop, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        DAS_ASSERT ( rightType.canCopy() &&
                "we are calling makeLocalCopy on a type, which can't be copied."
                "we should not be here, script compiler should have caught this during compilation."
                "compiler later will likely report internal compilation error.");
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, stackTop);
                return right;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = context.code->makeNode(rE->at, stackTop);
                return right;
            }
        }
        // now, to the regular copy
        auto left = context.code->makeNode(rE->at, stackTop);
        auto right = rE->simulate(context);
        if ( rightType.isHandle() ) {
            auto resN = rightType.annotation->simulateCopy(context, at, left,
                (!rightType.isRefType() && rightType.ref) ? rightType.annotation->simulateRef2Value(context, at, right) : right);
            if ( !resN ) {
                context.thisProgram->error("integration error, simulateCopy returned null", "", "",
                                           at, CompilationError::missing_node );
            }
            return resN;
        } else if ( rightType.isRef() ) {
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else {
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * makeCopy(const LineInfo & at, Context & context, const ExpressionPtr & lE, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        DAS_ASSERT ( rightType.canCopy() &&
                "we are calling makeCopy on a type, which can't be copied."
                "we should not be here, script compiler should have caught this during compilation."
                "compiler later will likely report internal compilation error.");
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = lE->simulate(context);
                return right;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = lE->simulate(context);
                return right;
            }
        }
        // now, to the regular copy
        auto left = lE->simulate(context);
        auto right = rE->simulate(context);
        if ( rightType.isHandle() ) {
            auto resN = rightType.annotation->simulateCopy(context, at, left,
                (!rightType.isRefType() && rightType.ref) ? rightType.annotation->simulateRef2Value(context, at, right) : right);
            if ( !resN ) {
                context.thisProgram->error("integration error, simulateCopy returned null", "", "",
                    at, CompilationError::missing_node );
            }
            return resN;
        } else if ( rightType.isRef() ) {
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else {
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * makeMove (const LineInfo & at, Context & context, const ExpressionPtr & lE, const ExpressionPtr & rE ) {
        const auto & rightType = *rE->type;
        // now, call with CMRES
        if ( rE->rtti_isCall() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = lE->simulate(context);
                return right;
            }
        }
        // now, invoke with CMRES
        if ( rE->rtti_isInvoke() ) {
            auto cll = static_pointer_cast(rE);
            if ( cll->isCopyOrMove() ) {
                SimNode_CallBase * right = (SimNode_CallBase *) rE->simulate(context);
                right->cmresEval = lE->simulate(context);
                return right;
            }
        }
        // now to the regular one
        if ( rightType.isRef() ) {
            auto left = lE->simulate(context);
            auto right = rE->simulate(context);
            return context.code->makeNode(at, left, right, rightType.getSizeOf());
        } else {
            // this here might happen during initialization, by moving value types
            // like var t simulate(context);
            auto right = rE->simulate(context);
            return context.code->makeValueNode(rightType.baseType, at, left, right);
        }
    }

    SimNode * Function::makeSimNode ( Context & context, const vector & ) {
        {
            if ( copyOnReturn || moveOnReturn ) {
                return context.code->makeNodeUnrollAny(int(arguments.size()), at);
            } else if ( fastCall ) {
                return context.code->makeNodeUnrollAny(int(arguments.size()), at);
            } else {
                return context.code->makeNodeUnrollAny(int(arguments.size()), at);
            }
        }
    }

    SimNode * Function::simulate (Context & context) const {
        if ( builtIn ) {
            DAS_ASSERTF(0, "can only simulate non built-in function");
            return nullptr;
        }
        for ( auto & ann : annotations ) {
            if ( ann->annotation->rtti_isFunctionAnnotation() ) {
                auto fann = (FunctionAnnotation *)(ann->annotation.get());
                string err;
                auto node = fann->simulate(&context, (Function*)this, ann->arguments, err);
                if ( !node ) {
                    if ( !err.empty() ) {
                        context.thisProgram->error("integration error, function failed to simulate", err, "",
                            at, CompilationError::missing_node );
                        return nullptr;
                    }
                } else {
                    return node;
                }
            }
        }
        if ( fastCall ) {
            DAS_ASSERT(totalStackSize == sizeof(Prologue) && "function can't allocate stack");
            DAS_ASSERT((result->isWorkhorseType() || result->isVoid()) && "fastcall can only return a workhorse type");
            DAS_ASSERT(body->rtti_isBlock() && "function must contain a block");
            auto block = static_pointer_cast(body);
            if ( block->list.size()==0 ) {
                DAS_ASSERT(block->inFunction && block->inFunction->result->isVoid() && "only void function produces fastcall NOP");
                return context.code->makeNode(block->at);
            }
            if ( block->list.back()->rtti_isReturn() ) {
                DAS_ASSERT(block->list.back()->rtti_isReturn() && "fastcall body expr is return");
                auto retE = static_pointer_cast(block->list.back());
                if ( retE->subexpr ) {
                    return retE->subexpr->simulate(context);
                } else {
                    return context.code->makeNode(retE->at);
                }
            } else {
                return block->list.back()->simulate(context);
            }
        } else {
#if DAS_DEBUGGER
            if ( context.thisProgram->getDebugger() ) {
                auto sbody = body->simulate(context);
                if ( !sbody->rtti_node_isBlock() ) {
                    auto block = context.code->makeNode(sbody->debugInfo);
                    block->total = 1;
                    block->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*1);
                    block->list[0] = sbody;
                    return block;
                } else {
                    return sbody;
                }
            } else {
                return body->simulate(context);
            }
#else
            return body->simulate(context);
#endif
        }
    }

    SimNode * Expression::trySimulate (Context &, uint32_t, const TypeDeclPtr &) const {
        return nullptr;
    }

    void ExprMakeLocal::setRefSp ( bool ref, bool cmres, uint32_t sp, uint32_t off ) {
        useStackRef = ref;
        useCMRES = cmres;
        doesNotNeedSp = true;
        doesNotNeedInit = true;
        stackTop = sp;
        extraOffset = off;
    }

    vector ExprMakeLocal::simulateLocal ( Context & /*context*/ ) const {
        return vector();
    }

    // variant

    void ExprMakeVariant::setRefSp ( bool ref, bool cmres, uint32_t sp, uint32_t off ) {
        ExprMakeLocal::setRefSp(ref, cmres, sp, off);
        int stride = makeType->getStride();
        // we go through all fields, and if its [[ ]] field
        // we tell it to piggy-back on our current sp, with appropriate offset
        int index = 0;
        for ( const auto & decl : variants ) {
            auto fieldVariant = makeType->findArgumentIndex(decl->name);
            DAS_ASSERT(fieldVariant!=-1 && "should have failed in type infer otherwise");
            auto fieldType = makeType->argTypes[fieldVariant];
            if ( decl->value->rtti_isMakeLocal() ) {
                auto fieldOffset = makeType->getVariantFieldOffset(fieldVariant);
                uint32_t offset =  extraOffset + index*stride + fieldOffset;
                auto mkl = static_pointer_cast(decl->value);
                mkl->setRefSp(ref, cmres, sp, offset);
            } else if ( decl->value->rtti_isCall() ) {
                auto cll = static_pointer_cast(decl->value);
                if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                    cll->doesNotNeedSp = true;
                }
            } else if ( decl->value->rtti_isInvoke() ) {
                auto cll = static_pointer_cast(decl->value);
                if ( cll->isCopyOrMove() ) {
                    cll->doesNotNeedSp = true;
                }
            }
            index++;
        }
    }

    vector ExprMakeVariant::simulateLocal (Context & context) const {
        vector simlist;
        int index = 0;
        int stride = makeType->getStride();
        // init with 0 it its 'default' initialization
        if ( stride && variants.empty() ) {
            int bytes = stride;
            SimNode * init0;
            if ( useCMRES ) {
                if ( bytes makeNodeUnrollNZ(bytes, at,extraOffset);
                } else {
                    init0 = context.code->makeNode(at,extraOffset,bytes);
                }
            } else if ( useStackRef ) {
                init0 = context.code->makeNode(at,stackTop,extraOffset,bytes);
            } else {
                init0 = context.code->makeNode(at,stackTop + extraOffset,bytes);
            }
            simlist.push_back(init0);
        }
        // now fields
        for ( const auto & decl : variants ) {
            auto fieldVariant = makeType->findArgumentIndex(decl->name);
            DAS_ASSERT(fieldVariant!=-1 && "should have failed in type infer otherwise");
            // lets set variant index
            uint32_t voffset = extraOffset + index*stride;
            auto vconst = make_smart(at, int32_t(fieldVariant));
            vconst->type = make_smart(Type::tInt);
            SimNode * svi;
            if ( useCMRES ) {
                svi = makeLocalCMResCopy(at,context,voffset,vconst);
            } else if (useStackRef) {
                svi = makeLocalRefCopy(at,context,stackTop,voffset,vconst);
            } else {
                svi = makeLocalCopy(at,context,stackTop+voffset,vconst);
            }
            simlist.push_back(svi);
            // field itself
            auto fieldOffset = makeType->getVariantFieldOffset(fieldVariant);
            uint32_t offset =  voffset + fieldOffset;
            SimNode * cpy;
            if ( decl->value->rtti_isMakeLocal() ) {
                // so what happens here, is we ask it for the generated commands and append it to this list only
                auto mkl = static_pointer_cast(decl->value);
                auto lsim = mkl->simulateLocal(context);
                simlist.insert(simlist.end(), lsim.begin(), lsim.end());
                continue;
            } else if ( useCMRES ) {
                if ( decl->moveSemantics ){
                    cpy = makeLocalCMResMove(at,context,offset,decl->value);
                } else {
                    cpy = makeLocalCMResCopy(at,context,offset,decl->value);
                }
            } else if ( useStackRef ) {
                if ( decl->moveSemantics ){
                    cpy = makeLocalRefMove(at,context,stackTop,offset,decl->value);
                } else {
                    cpy = makeLocalRefCopy(at,context,stackTop,offset,decl->value);
                }
            } else {
                if ( decl->moveSemantics ){
                    cpy = makeLocalMove(at,context,stackTop+offset,decl->value);
                } else {
                    cpy = makeLocalCopy(at,context,stackTop+offset,decl->value);
                }
            }
            if ( !cpy ) {
                context.thisProgram->error("internal compilation error, can't generate structure initialization", "", "", at);
            }
            simlist.push_back(cpy);
            index++;
        }
        return simlist;
    }

    SimNode * ExprMakeVariant::simulate (Context & context) const {
        SimNode_Block * block;
        if ( useCMRES ) {
            block = context.code->makeNode(at);
        } else {
            block = context.code->makeNode(at, stackTop);
        }
        auto simlist = simulateLocal(context);
        block->total = int(simlist.size());
        block->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*block->total);
        for ( uint32_t i = 0; i != block->total; ++i )
            block->list[i] = simlist[i];
        return block;
    }

    // structure

    void ExprMakeStruct::setRefSp ( bool ref, bool cmres, uint32_t sp, uint32_t off ) {
        ExprMakeLocal::setRefSp(ref, cmres, sp, off);
        // if it's a handle type, we can't reuse the make-local chain
        if ( makeType->baseType == Type::tHandle ) return;
        // we go through all fields, and if its [[ ]] field
        // we tell it to piggy-back on our current sp, with appropriate offset
        int total = int(structs.size());
        int stride = makeType->getStride();
        for ( int index=0; index != total; ++index ) {
            auto & fields = structs[index];
            for ( const auto & decl : *fields ) {
                auto field = makeType->structType->findField(decl->name);
                DAS_ASSERT(field && "should have failed in type infer otherwise");
                if ( decl->value->rtti_isMakeLocal() ) {
                    uint32_t offset =  extraOffset + index*stride + field->offset;
                    auto mkl = static_pointer_cast(decl->value);
                    mkl->setRefSp(ref, cmres, sp, offset);
                } else if ( decl->value->rtti_isCall() ) {
                    auto cll = static_pointer_cast(decl->value);
                    if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                        cll->doesNotNeedSp = true;
                    }
                } else if ( decl->value->rtti_isInvoke() ) {
                    auto cll = static_pointer_cast(decl->value);
                    if ( cll->isCopyOrMove() ) {
                        cll->doesNotNeedSp = true;
                    }
                }
            }
        }
    }

    vector ExprMakeStruct::simulateLocal (Context & context) const {
        vector simlist;
        // init with 0
        int total = int(structs.size());
        int stride = makeType->getStride();
        if ( !doesNotNeedInit && !initAllFields && stride ) {
            int bytes = das::max(total,1) * stride;
            SimNode * init0;
            if ( useCMRES ) {
                if ( bytes makeNodeUnrollNZ(bytes, at,extraOffset);
                } else {
                    init0 = context.code->makeNode(at,extraOffset,bytes);
                }
            } else if ( useStackRef ) {
                init0 = context.code->makeNode(at,stackTop,extraOffset,bytes);
            } else {
                init0 = context.code->makeNode(at,stackTop + extraOffset,bytes);
            }
            simlist.push_back(init0);
        }
        if ( makeType->baseType == Type::tStructure ) {
            for ( int index=0; index != total; ++index ) {
                auto & fields = structs[index];
                for ( const auto & decl : *fields ) {
                    auto field = makeType->structType->findField(decl->name);
                    DAS_ASSERT(field && "should have failed in type infer otherwise");
                    uint32_t offset =  extraOffset + index*stride + field->offset;
                    SimNode * cpy;
                    if ( decl->value->rtti_isMakeLocal() ) {
                        // so what happens here, is we ask it for the generated commands and append it to this list only
                        auto mkl = static_pointer_cast(decl->value);
                        auto lsim = mkl->simulateLocal(context);
                        simlist.insert(simlist.end(), lsim.begin(), lsim.end());
                        continue;
                    } else if ( useCMRES ) {
                        if ( decl->moveSemantics ){
                            cpy = makeLocalCMResMove(at,context,offset,decl->value);
                        } else {
                            cpy = makeLocalCMResCopy(at,context,offset,decl->value);
                        }
                    } else if ( useStackRef ) {
                        if ( decl->moveSemantics ){
                            cpy = makeLocalRefMove(at,context,stackTop,offset,decl->value);
                        } else {
                            cpy = makeLocalRefCopy(at,context,stackTop,offset,decl->value);
                        }
                    } else {
                        if ( decl->moveSemantics ){
                            cpy = makeLocalMove(at,context,stackTop+offset,decl->value);
                        } else {
                            cpy = makeLocalCopy(at,context,stackTop+offset,decl->value);
                        }
                    }
                    if ( !cpy ) {
                        context.thisProgram->error("internal compilation error, can't generate structure initialization", "", "", at);
                    }
                    simlist.push_back(cpy);
                }
            }
        } else {
            auto ann = makeType->annotation;
            // making fake variable, which points to out field
            string fakeName = "__makelocal";
            auto fakeVariable = make_smart();
            fakeVariable->name = fakeName;
            fakeVariable->type = make_smart(Type::tHandle);
            fakeVariable->type->annotation = ann;
            fakeVariable->at = at;
            if ( useCMRES ) {
                fakeVariable->aliasCMRES = true;
            } else if ( useStackRef ) {
                fakeVariable->stackTop = stackTop + extraOffset;
                fakeVariable->type->ref = true;
                if ( total != 1 ) {
                    fakeVariable->type->dim.push_back(total);
                }
            }
            fakeVariable->generated = true;
            // make fake ExprVar which is that field
            auto fakeVar = make_smart(at, fakeName);
            fakeVar->type = fakeVariable->type;
            fakeVar->variable = fakeVariable;
            fakeVar->local = true;
            // make fake expression
            ExpressionPtr fakeExpr = fakeVar;
            smart_ptr indexExpr;
            if ( useStackRef && total > 1 ) {
                // if its stackRef with multiple indices, its actually var[total], and lookup is var[index]
                indexExpr = make_smart(at, 0);
                indexExpr->type = make_smart(Type::tInt);
                fakeExpr = make_smart(at, fakeExpr, indexExpr);
                fakeExpr->type = make_smart(Type::tHandle);
                fakeExpr->type->annotation = ann;
                fakeExpr->type->ref = true;
            }
            for ( int index=0; index != total; ++index ) {
                auto & fields = structs[index];
                // adjust var for index
                if ( useCMRES ) {
                    fakeVariable->stackTop = extraOffset + index*stride;
                } else if ( useStackRef ) {
                    if ( total > 1 ) {
                        indexExpr->value = cast::from(index);
                    }
                } else {
                    fakeVariable->stackTop = stackTop + extraOffset + index*stride;
                }
                // now, setup fields
                for ( const auto & decl : *fields ) {
                    auto fieldType = ann->makeFieldType(decl->name, false);
                    DAS_ASSERT(fieldType && "how did this infer?");
                    uint32_t fieldSize = fieldType->getSizeOf();
                    SimNode * cpy = nullptr;
                    auto left = ann->simulateGetField(decl->name, context, at, fakeExpr);
                    auto right = decl->value->simulate(context);
                    if ( !decl->value->type->isRef() ) {
                        if ( decl->value->type->isHandle() ) {
                            auto rightType = decl->value->type;
                            cpy = rightType->annotation->simulateCopy(context, at, left,
                                (!rightType->isRefType() && rightType->ref) ? rightType->annotation->simulateRef2Value(context, at, right) : right);
                            if ( !cpy ) {
                                context.thisProgram->error("integration error, simulateCopy returned null", "", "",
                                                        at, CompilationError::missing_node );
                            }
                        } else {
                            cpy = context.code->makeValueNode(decl->value->type->baseType, decl->at, left, right);
                        }
                    } else if ( decl->moveSemantics ) {
                        cpy = context.code->makeNode(decl->at, left, right, fieldSize);
                    } else {
                        cpy = context.code->makeNode(decl->at, left, right, fieldSize);
                    }
                    simlist.push_back(cpy);
                }
            }
        }
        if ( block ) {
            /*
                TODO: optimize
                    there is no point in making fake invoke expression, we can replace 'self' with fake variable we've made
                    however this needs to happen during infer, and this needs to have different visitor,
                    so that stack is allocated properly in subexpressions etc
            */
            // making fake variable, which points to entire structure
            string fakeName = "__makelocal";
            auto fakeVariable = make_smart();
            fakeVariable->name = fakeName;
            fakeVariable->type = make_smart(*type);
            if ( useCMRES ) {
                fakeVariable->aliasCMRES = true;
            } else if ( useStackRef ) {
                fakeVariable->stackTop = stackTop + extraOffset;
                fakeVariable->type->ref = true;
            } else {
                fakeVariable->stackTop = stackTop + extraOffset;
            }
            fakeVariable->generated = true;
            // make fake ExprVar which is that field
            auto fakeVar = make_smart(at, fakeName);
            fakeVar->type = fakeVariable->type;
            fakeVar->variable = fakeVariable;
            fakeVar->local = true;
            // make fake invoke expression
            auto fakeInvoke = make_smart(at,"invoke");
            fakeInvoke->arguments.push_back(block);
            fakeInvoke->arguments.push_back(fakeVar);
            // simulate it
            auto simI = fakeInvoke->simulate(context);
            simlist.push_back(simI);
        }
        return simlist;
    }

    SimNode * ExprMakeStruct::simulate (Context & context) const {
        SimNode_Block * blk;
        if ( useCMRES ) {
            blk = context.code->makeNode(at);
        } else {
            blk = context.code->makeNode(at, stackTop);
        }
        auto simlist = simulateLocal(context);
        blk->total = int(simlist.size());
        blk->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*blk->total);
        for ( uint32_t i = 0; i != blk->total; ++i )
            blk->list[i] = simlist[i];
        return blk;
    }

    // make array

    void ExprMakeArray::setRefSp ( bool ref, bool cmres, uint32_t sp, uint32_t off ) {
        ExprMakeLocal::setRefSp(ref, cmres, sp, off);
        int total = int(values.size());
        uint32_t stride = recordType->getSizeOf();
        for ( int index=0; index != total; ++index ) {
            auto & val = values[index];
            if ( val->rtti_isMakeLocal() ) {
                uint32_t offset =  extraOffset + index*stride;
                auto mkl = static_pointer_cast(val);
                mkl->setRefSp(ref, cmres, sp, offset);
            } else if ( val->rtti_isCall() ) {
                auto cll = static_pointer_cast(val);
                if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                    cll->doesNotNeedSp = true;
                }
            } else if ( val->rtti_isInvoke() ) {
                auto cll = static_pointer_cast(val);
                if ( cll->isCopyOrMove() ) {
                    cll->doesNotNeedSp = true;
                }
            }
        }
    }

    vector ExprMakeArray::simulateLocal (Context & context) const {
        vector simlist;
        // init with 0
        int total = int(values.size());
        uint32_t stride = recordType->getSizeOf();
        if ( !doesNotNeedInit && !initAllFields ) {
            int bytes = total * stride;
            SimNode * init0;
            if ( useCMRES ) {
                if ( bytes makeNodeUnrollNZ(bytes, at,extraOffset);
                } else {
                    init0 = context.code->makeNode(at,extraOffset,bytes);
                }
            } else if ( useStackRef ) {
                init0 = context.code->makeNode(at,stackTop,extraOffset,stride * total);
            } else {
                init0 = context.code->makeNode(at,stackTop + extraOffset,stride * total);
            }
            simlist.push_back(init0);
        }
        for ( int index=0; index != total; ++index ) {
            auto & val = values[index];
            uint32_t offset = extraOffset + index*stride;
            SimNode * cpy;
            if ( val->rtti_isMakeLocal() ) {
                // so what happens here, is we ask it for the generated commands and append it to this list only
                auto mkl = static_pointer_cast(val);
                auto lsim = mkl->simulateLocal(context);
                simlist.insert(simlist.end(), lsim.begin(), lsim.end());
                continue;
            } else if ( useCMRES ) {
                if (val->type->canCopy()) {
                    cpy = makeLocalCMResCopy(at, context, offset, val);
                } else {
                    cpy = makeLocalCMResMove(at, context, offset, val);
                }
            } else if ( useStackRef ) {
                if (val->type->canCopy()) {
                    cpy = makeLocalRefCopy(at, context, stackTop, offset, val);
                } else {
                    cpy = makeLocalRefMove(at, context, stackTop, offset, val);
                }
            } else {
                if (val->type->canCopy()) {
                    cpy = makeLocalCopy(at, context, stackTop + offset, val);
                } else {
                    cpy = makeLocalMove(at, context, stackTop + offset, val);
                }
            }
            if ( !cpy ) {
                context.thisProgram->error("internal compilation error, can't generate array initialization", "", "", at);
            }
            simlist.push_back(cpy);
        }
        return simlist;
    }

    SimNode * ExprMakeArray::simulate (Context & context) const {
        SimNode_Block * block;
        if ( useCMRES ) {
            block = context.code->makeNode(at);
        } else {
            block = context.code->makeNode(at, stackTop);
        }
        auto simlist = simulateLocal(context);
        block->total = int(simlist.size());
        block->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*block->total);
        for ( uint32_t i = 0; i != block->total; ++i )
            block->list[i] = simlist[i];
        return block;
    }

    // make tuple

    void ExprMakeTuple::setRefSp ( bool ref, bool cmres, uint32_t sp, uint32_t off ) {
        ExprMakeLocal::setRefSp(ref, cmres, sp, off);
        int total = int(values.size());
        for ( int index=0; index != total; ++index ) {
            auto & val = values[index];
            if ( val->rtti_isMakeLocal() ) {
                uint32_t offset =  extraOffset + makeType->getTupleFieldOffset(index);
                auto mkl = static_pointer_cast(val);
                mkl->setRefSp(ref, cmres, sp, offset);
            } else if ( val->rtti_isCall() ) {
                auto cll = static_pointer_cast(val);
                if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                    cll->doesNotNeedSp = true;
                }
            } else if ( val->rtti_isInvoke() ) {
                auto cll = static_pointer_cast(val);
                if ( cll->isCopyOrMove() ) {
                    cll->doesNotNeedSp = true;
                }
            }
        }
    }

    vector ExprMakeTuple::simulateLocal (Context & context) const {
        vector simlist;
        // init with 0
        int total = int(values.size());
        if ( !doesNotNeedInit && !initAllFields ) {
            uint32_t sizeOf = makeType->getSizeOf();
            SimNode * init0;
            if ( useCMRES ) {
                if ( sizeOf makeNodeUnrollNZ(sizeOf, at,extraOffset);
                } else {
                    init0 = context.code->makeNode(at,extraOffset,sizeOf);
                }
            } else if ( useStackRef ) {
                init0 = context.code->makeNode(at,stackTop,extraOffset,sizeOf);
            } else {
                init0 = context.code->makeNode(at,stackTop + extraOffset,sizeOf);
            }
            simlist.push_back(init0);
        }
        for ( int index=0; index != total; ++index ) {
            auto & val = values[index];
            uint32_t offset = extraOffset + makeType->getTupleFieldOffset(index);
            SimNode * cpy;
            if ( val->rtti_isMakeLocal() ) {
                // so what happens here, is we ask it for the generated commands and append it to this list only
                auto mkl = static_pointer_cast(val);
                auto lsim = mkl->simulateLocal(context);
                simlist.insert(simlist.end(), lsim.begin(), lsim.end());
                continue;
            } else if ( useCMRES ) {
                if (val->type->canCopy()) {
                    cpy = makeLocalCMResCopy(at, context, offset, val);
                } else {
                    cpy = makeLocalCMResMove(at, context, offset, val);
                }
            } else if ( useStackRef ) {
                if (val->type->canCopy()) {
                    cpy = makeLocalRefCopy(at, context, stackTop, offset, val);
                } else {
                    cpy = makeLocalRefMove(at, context, stackTop, offset, val);
                }
            } else {
                if (val->type->canCopy()) {
                    cpy = makeLocalCopy(at, context, stackTop + offset, val);
                } else {
                    cpy = makeLocalMove(at, context, stackTop + offset, val);
                }
            }
            if ( !cpy ) {
                context.thisProgram->error("internal compilation error, can't generate array initialization", "", "", at);
            }
            simlist.push_back(cpy);
        }
        return simlist;
    }


    SimNode * ExprMakeTuple::simulate (Context & context) const {
        SimNode_Block * block;
        if ( useCMRES ) {
            block = context.code->makeNode(at);
        } else {
            block = context.code->makeNode(at, stackTop);
        }
        auto simlist = simulateLocal(context);
        block->total = int(simlist.size());
        block->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*block->total);
        for ( uint32_t i = 0; i != block->total; ++i )
            block->list[i] = simlist[i];
        return block;
    }

    // reader

    SimNode * ExprReader::simulate (Context & context) const {
        context.thisProgram->error("internal compilation error, calling 'simulate' on reader", "", "", at);
        return nullptr;
    }

    // label

    SimNode * ExprLabel::simulate (Context & context) const {
        context.thisProgram->error("internal compilation error, calling 'simulate' on label", "", "", at);
        return nullptr;
    }

    // goto

    SimNode * ExprGoto::simulate (Context & context) const {
        if ( subexpr ) {
            return context.code->makeNode(at, subexpr->simulate(context));
        } else {
            return context.code->makeNode(at,label);
        }
    }

    // r2v

    SimNode * ExprRef2Value::GetR2V ( Context & context, const LineInfo & at, const TypeDeclPtr & type, SimNode * expr ) {
        if ( type->isHandle() ) {
            auto resN = type->annotation->simulateRef2Value(context, at, expr);
            if ( !resN ) {
                context.thisProgram->error("integration error, simulateRef2Value returned null", "", "",
                                           at, CompilationError::missing_node );
            }
            return resN;
        } else {
            if ( type->isRefType() ) {
                return expr;
            } else {
                return context.code->makeValueNode(type->baseType, at, expr);
            }
        }
    }

    SimNode * ExprRef2Value::simulate (Context & context) const {
        return GetR2V(context, at, type, subexpr->simulate(context));
    }

    SimNode * ExprAddr::simulate (Context & context) const {
        if ( !func ) {
            context.thisProgram->error("internal compilation error, ExprAddr func is null", "", "", at);
            return nullptr;
        } else if ( func->indexerror("internal compilation error, ExprAddr func->index is unused", "", "", at);
            return nullptr;

        }
        union {
            uint64_t    mnh;
            vec4f       cval;
        } temp;
        temp.cval = v_zero();
        if ( func->module->isSolidContext ) {
            DAS_ASSERT(func->index>=0 && "address of unsued function? how?");
            temp.mnh = func->index;
            return context.code->makeNode(at,temp.cval);
        } else {
            temp.mnh = func->getMangledNameHash();
            return context.code->makeNode(at,temp.cval);
        }
    }

    SimNode * ExprPtr2Ref::simulate (Context & context) const {
        if ( unsafeDeref ) {
            return subexpr->simulate(context);
        } else {
            return context.code->makeNode(at,subexpr->simulate(context));
        }
    }

    SimNode * ExprRef2Ptr::simulate (Context & context) const {
        return subexpr->simulate(context);
    }

    SimNode * ExprNullCoalescing::simulate (Context & context) const {
        if ( type->isRef() ) {
            return context.code->makeNode(at,subexpr->simulate(context),defaultValue->simulate(context));
        } else {
            return context.code->makeValueNode(type->baseType,at,subexpr->simulate(context),defaultValue->simulate(context));
        }
    }

    SimNode * ExprConst::simulate (Context & context) const {
        return context.code->makeNode(at,value);
    }

    SimNode * ExprConstEnumeration::simulate (Context & context) const {
        return context.code->makeNode(at, value);
    }

    SimNode * ExprConstString::simulate (Context & context) const {
        if ( !text.empty() ) {
            char* str = context.constStringHeap->allocateString(text);
            return context.code->makeNode(at, str);
        } else {
            return context.code->makeNode(at, nullptr);
        }
    }

    SimNode * ExprStaticAssert::simulate (Context &) const {
        return nullptr;
    }

    SimNode * ExprAssert::simulate (Context & context) const {
        string message;
        if ( arguments.size()==2 && arguments[1]->rtti_isStringConstant() )
            message = static_pointer_cast(arguments[1])->getValue();
        return context.code->makeNode(at,arguments[0]->simulate(context),context.constStringHeap->allocateString(message));
    }

    struct SimNode_AstGetExpression : SimNode_CallBase {
        DAS_PTR_NODE;
        SimNode_AstGetExpression ( const LineInfo & at, const ExpressionPtr & e, char * d )
            : SimNode_CallBase(at) {
            expr = e.get();
            descr = d;
        }
        virtual SimNode * copyNode ( Context & context, NodeAllocator * code ) override {
            auto that = (SimNode_AstGetExpression *) SimNode::copyNode(context, code);
            that->descr = code->allocateName(descr);
            return that;
        }
        virtual SimNode * visit ( SimVisitor & vis ) override {
            V_BEGIN();
            V_OP(AstGetExpression);
            V_ARG(descr);
            V_END();
        }
        __forceinline char * compute(Context &) {
            DAS_PROFILE_NODE
            return (char *) expr->clone().orphan();
        }
        Expression *  expr;   // requires RTTI
        char *        descr;
    };

    SimNode * ExprQuote::simulate (Context & context) const {
        DAS_ASSERTF(arguments.size()==1,"Quote expects to return only one ExpressionPtr."
        "We should not be here, since typeinfer should catch the mismatch.");
        TextWriter ss;
        ss allocateName(ss.str());
        return context.code->makeNode(at, arguments[0], descr);
    }

    SimNode * ExprDebug::simulate (Context & context) const {
        TypeInfo * pTypeInfo = context.thisHelper->makeTypeInfo(nullptr, arguments[0]->type);
        string message;
        if ( arguments.size()==2 && arguments[1]->rtti_isStringConstant() )
            message = static_pointer_cast(arguments[1])->getValue();
        return context.code->makeNode(at,
                                               arguments[0]->simulate(context),
                                               pTypeInfo,
                                               context.constStringHeap->allocateString(message));
    }

    SimNode * ExprMemZero::simulate (Context & context) const {
        const auto & subexpr = arguments[0];
        uint32_t dataSize = subexpr->type->getSizeOf();
        return context.code->makeNode(at, subexpr->simulate(context), dataSize);
    }

    SimNode * ExprMakeGenerator::simulate (Context & context) const {
        DAS_ASSERTF(0, "we should not be here ever, ExprMakeGenerator should completly fold during type inference.");
        context.thisProgram->error("internal compilation error, generating node for ExprMakeGenerator", "", "", at);
        return nullptr;
    }

    SimNode * ExprYield::simulate (Context & context) const {
        DAS_ASSERTF(0, "we should not be here ever, ExprYield should completly fold during type inference.");
        context.thisProgram->error("internal compilation error, generating node for ExprYield", "", "", at);
        return nullptr;
    }

    SimNode * ExprArrayComprehension::simulate (Context & context) const {
        DAS_ASSERTF(0, "we should not be here ever, ExprArrayComprehension should completly fold during type inference.");
        context.thisProgram->error("internal compilation error, generating node for ExprArrayComprehension", "", "", at);
        return nullptr;
    }

    SimNode * ExprMakeBlock::simulate (Context & context) const {
        auto blk = static_pointer_cast(block);
        uint32_t argSp = blk->stackTop;
        auto info = context.thisHelper->makeInvokeableTypeDebugInfo(blk->makeBlockType(),blk->at);
        if ( context.thisProgram->getDebugger() || context.thisProgram->options.getBoolOption("gc",false) ) {
            context.thisHelper->appendLocalVariables(info, (Expression *)this);
        }
        return context.code->makeNode(at,block->simulate(context),argSp,stackTop,info);
    }

    bool ExprInvoke::isCopyOrMove() const {
        auto blockT = arguments[0]->type;
        return blockT->firstType && blockT->firstType->isRefType() && !blockT->firstType->ref;
    }

    SimNode * ExprInvoke::simulate (Context & context) const {
        auto blockT = arguments[0]->type;
        SimNode_CallBase * pInvoke;
        {
            if ( isCopyOrMove() ) {
                auto getSp = context.code->makeNode(at,stackTop);
                if ( blockT->baseType==Type::tBlock ) {
                    pInvoke = (SimNode_CallBase *) context.code->makeNodeUnrollAny(
                                                        int(arguments.size()), at, getSp);
                } else if ( blockT->baseType==Type::tFunction ) {
                    pInvoke = (SimNode_CallBase *) context.code->makeNodeUnrollAny(
                                                        int(arguments.size()), at, getSp);
                } else {
                    pInvoke = (SimNode_CallBase *) context.code->makeNodeUnrollAny(
                                                        int(arguments.size()), at, getSp);
                }
            } else {
                if ( blockT->baseType==Type::tBlock ) {
                    pInvoke = (SimNode_CallBase *) context.code->makeNodeUnrollAny(int(arguments.size()),at);
                } else if ( blockT->baseType==Type::tFunction ) {
                    pInvoke = (SimNode_CallBase *) context.code->makeNodeUnrollAny(int(arguments.size()),at);
                } else {
                    pInvoke = (SimNode_CallBase *) context.code->makeNodeUnrollAny(int(arguments.size()),at);
                }
            }
        }
        pInvoke->debugInfo = at;
        if ( int nArg = (int) arguments.size() ) {
            pInvoke->arguments = (SimNode **) context.code->allocate(nArg * sizeof(SimNode *));
            pInvoke->nArguments = nArg;
            for ( int a=0; a!=nArg; ++a ) {
                pInvoke->arguments[a] = arguments[a]->simulate(context);
            }
        } else {
            pInvoke->arguments = nullptr;
            pInvoke->nArguments = 0;
        }
        return pInvoke;
    }

    SimNode * ExprErase::simulate (Context & context) const {
        auto cont = arguments[0]->simulate(context);
        auto val = arguments[1]->simulate(context);
        if ( arguments[0]->type->isGoodTableType() ) {
            uint32_t valueTypeSize = arguments[0]->type->secondType->getSizeOf();
            return context.code->makeValueNode(arguments[0]->type->firstType->baseType, at, cont, val, valueTypeSize);
        } else {
            DAS_ASSERTF(0, "we should not even be here. erase can only accept tables. infer type should have failed.");
            context.thisProgram->error("internal compilation error, generating erase for non-table type", "", "", at);
            return nullptr;
        }
    }

    SimNode * ExprFind::simulate (Context & context) const {
        auto cont = arguments[0]->simulate(context);
        auto val = arguments[1]->simulate(context);
        if ( arguments[0]->type->isGoodTableType() ) {
            uint32_t valueTypeSize = arguments[0]->type->secondType->getSizeOf();
            return context.code->makeValueNode(arguments[0]->type->firstType->baseType, at, cont, val, valueTypeSize);
        } else {
            DAS_ASSERTF(0, "we should not even be here. find can only accept tables. infer type should have failed.");
            context.thisProgram->error("internal compilation error, generating find for non-table type", "", "", at);
            return nullptr;
        }
    }

    SimNode * ExprKeyExists::simulate (Context & context) const {
        auto cont = arguments[0]->simulate(context);
        auto val = arguments[1]->simulate(context);
        if ( arguments[0]->type->isGoodTableType() ) {
            uint32_t valueTypeSize = arguments[0]->type->secondType->getSizeOf();
            return context.code->makeValueNode(arguments[0]->type->firstType->baseType, at, cont, val, valueTypeSize);
        } else {
            DAS_ASSERTF(0, "we should not even be here. find can only accept tables. infer type should have failed.");
            context.thisProgram->error("internal compilation error, generating find for non-table type", "", "", at);
            return nullptr;
        }
    }

    SimNode * ExprIs::simulate (Context & context) const {
        DAS_ASSERTF(0, "we should not even be here. 'is' should resolve to const during infer pass.");
        context.thisProgram->error("internal compilation error, generating 'is'", "", "", at);
        return nullptr;
    }

    SimNode * ExprTypeDecl::simulate (Context & context) const {
        return context.code->makeNode(at,v_zero());
    }

    SimNode * ExprTypeInfo::simulate (Context & context) const {
        if ( !macro ) {
            DAS_ASSERTF(0, "we should not even be here. typeinfo should resolve to const during infer pass.");
            context.thisProgram->error("internal compilation error, generating typeinfo(...)", "", "", at);
            return nullptr;
        } else {
            string errors;
            auto node = macro->simluate(&context, (Expression*)this, errors);
            if ( !node || !errors.empty() ) {
                context.thisProgram->error("typeinfo(" + trait + "...) macro generated no node; " + errors,
                    "", "", at, CompilationError::typeinfo_macro_error);
            }
            return node;
        }
    }

    SimNode * ExprDelete::simulate (Context & context) const {
        uint32_t total = uint32_t(subexpr->type->getCountOf());
        DAS_ASSERTF(total==1,"we should not be deleting more than one at a time");
        auto sube = subexpr->simulate(context);
        if ( subexpr->type->baseType==Type::tArray ) {
            auto stride = subexpr->type->firstType->getSizeOf();
            return context.code->makeNode(at, sube, total, stride);
        } else if ( subexpr->type->baseType==Type::tTable ) {
            auto vts_add_kts = subexpr->type->firstType->getSizeOf() +
                subexpr->type->secondType->getSizeOf();
            return context.code->makeNode(at, sube, total, vts_add_kts);
        } else if ( subexpr->type->baseType==Type::tPointer ) {
            if ( subexpr->type->firstType->baseType==Type::tStructure ) {
                if ( subexpr->type->firstType->structType->isClass ) {
                    if ( sizeexpr ) {
                        auto sze = sizeexpr->simulate(context);
                        return context.code->makeNode(at, sube, total, sze);
                    } else {
                        context.thisProgram->error("internal compiler error, SimNode_DeleteClassPtr needs size expression", "", "",
                                                at, CompilationError::missing_node );
                        return nullptr;
                    }
                } else {
                    auto structSize = subexpr->type->firstType->getSizeOf();
                    bool persistent = subexpr->type->firstType->structType->persistent;
                    bool isLambda = subexpr->type->firstType->structType->isLambda;
                    return context.code->makeNode(at, sube, total, structSize, persistent, isLambda);
                }
            } else if ( subexpr->type->firstType->baseType==Type::tTuple ) {
                auto structSize = subexpr->type->firstType->getSizeOf();
                return context.code->makeNode(at, sube, total, structSize, false, false);
            } else if ( subexpr->type->firstType->baseType==Type::tVariant ) {
                auto structSize = subexpr->type->firstType->getSizeOf();
                return context.code->makeNode(at, sube, total, structSize, false, false);
            } else {
                auto ann = subexpr->type->firstType->annotation;
                DAS_ASSERT(ann->canDeletePtr() && "has to be able to delete ptr");
                auto resN = ann->simulateDeletePtr(context, at, sube, total);
                if ( !resN ) {
                    context.thisProgram->error("integration error, simulateDelete returned null", "", "",
                                               at, CompilationError::missing_node );
                }
                return resN;
            }
        } else if ( subexpr->type->baseType==Type::tHandle ) {
            auto ann = subexpr->type->annotation;
            DAS_ASSERT(ann->canDelete() && "has to be able to delete");
            auto resN =  ann->simulateDelete(context, at, sube, total);
            if ( !resN ) {
                context.thisProgram->error("integration error, simulateDelete returned null", "", "",
                                           at, CompilationError::missing_node );
            }
            return resN;
        } else if ( subexpr->type->baseType==Type::tLambda ) {
            return context.code->makeNode(at, sube, total);
        } else {
            DAS_ASSERTF(0, "we should not be here. this is delete for unsupported type. infer types should have failed.");
            context.thisProgram->error("internal compilation error, generating node for unsupported ExprDelete", "", "", at);
            return nullptr;
        }
    }
    SimNode * ExprCast::trySimulate (Context & context, uint32_t extraOffset, const TypeDeclPtr & r2vType ) const {
        return subexpr->trySimulate(context, extraOffset, r2vType);
    }

    SimNode * ExprCast::simulate (Context & context) const {
        return subexpr->simulate(context);
    }

    SimNode * ExprAscend::simulate (Context & context) const {
        auto se = subexpr->simulate(context);
        auto bytes = subexpr->type->getSizeOf();
        TypeInfo * typeInfo = nullptr;
        if ( needTypeInfo ) {
            typeInfo = context.thisHelper->makeTypeInfo(nullptr, subexpr->type);
        }
        if ( subexpr->type->baseType==Type::tHandle ) {
            DAS_ASSERTF(useStackRef,"new of handled type should always be over stackref");
            auto ne = subexpr->type->annotation->simulateGetNew(context, at);
            return context.code->makeNode(at, se, ne, bytes, stackTop);
        } else {
            bool peristent = false;
            if ( subexpr->type->baseType==Type::tStructure ) {
                peristent = subexpr->type->structType->persistent;
            }
            if ( useStackRef ) {
                return context.code->makeNode(at, se, bytes, stackTop, typeInfo, peristent);
            } else {
                return context.code->makeNode(at, se, bytes, typeInfo, peristent);
            }
        }
    }

    SimNode * ExprNew::simulate (Context & context) const {
        SimNode * newNode;
        if ( typeexpr->baseType == Type::tHandle ) {
            DAS_ASSERT(typeexpr->annotation->canNew() && "how???");
            if ( initializer ) {
                int32_t bytes = type->firstType->getSizeOf();
                auto pCall = static_cast(func->makeSimNode(context,arguments));
                ExprCall::simulateCall(func, this, context, pCall);
                pCall->cmresEval = context.code->makeNode(at,bytes,true);
                return pCall;
            } else {
                newNode = typeexpr->annotation->simulateGetNew(context, at);
                if ( !newNode ) {
                    context.thisProgram->error("integration error, simulateGetNew returned null", "", "",
                                            at, CompilationError::missing_node );
                }
            }
        } else {
            bool persistent = false;
            if ( typeexpr->baseType == Type::tStructure ) {
                persistent = typeexpr->structType->persistent;
            }
            int32_t bytes = type->firstType->getSizeOf();
            if ( initializer ) {
                auto pCall = (SimNode_CallBase *) context.code->makeNodeUnrollAny(
                    int(arguments.size()),at,bytes,persistent);
                pCall->cmresEval = nullptr;
                newNode = ExprCall::simulateCall(func, this, context, pCall);
            } else {
                newNode = context.code->makeNode(at,bytes,persistent);
            }
        }
        if ( type->dim.size() ) {
            uint32_t count = type->getCountOf();
            return context.code->makeNode(at,newNode,stackTop,count);
        } else {
            return newNode;
        }
    }

    SimNode * ExprAt::trySimulate (Context & context, uint32_t extraOffset, const TypeDeclPtr & r2vType ) const {
        if ( subexpr->type->isVectorType() ) {
            return nullptr;
        } else if ( subexpr->type->isGoodTableType() ) {
            return nullptr;
        } else if ( subexpr->type->isHandle() ) {
            SimNode * result;
            if ( r2vType->baseType!=Type::none ) {
                result = subexpr->type->annotation->simulateGetAtR2V(context, at, r2vType, subexpr, index, extraOffset);
                if ( !result ) {
                    context.thisProgram->error("integration error, simulateGetAtR2V returned null", "", "",
                                               at, CompilationError::missing_node );
                }
            } else {
                result = subexpr->type->annotation->simulateGetAt(context, at, r2vType, subexpr, index, extraOffset);
                if ( !result ) {
                    context.thisProgram->error("integration error, simulateGetAt returned null", "", "",
                                               at, CompilationError::missing_node );
                }
            }
            return result;
        } else if ( subexpr->type->isGoodArrayType() ) {
            auto prv = subexpr->simulate(context);
            auto pidx = index->simulate(context);
            uint32_t stride = subexpr->type->firstType->getSizeOf();
            if ( r2vType->baseType!=Type::none ) {
                return context.code->makeValueNode(r2vType->baseType, at, prv, pidx, stride, extraOffset);
            } else {
                return context.code->makeNode(at, prv, pidx, stride, extraOffset);
            }
        } else if ( subexpr->type->isPointer() ) {
            uint32_t range = 0xffffffff;
            uint32_t stride = subexpr->type->firstType->getSizeOf();
            auto prv = subexpr->simulate(context);
            auto pidx = index->simulate(context);
            if ( r2vType->baseType!=Type::none ) {
                return context.code->makeValueNode(r2vType->baseType, at, prv, pidx, stride, extraOffset, range);
            } else {
                return context.code->makeNode(at, prv, pidx, stride, extraOffset, range);
            }
        } else {
            uint32_t range = subexpr->type->dim[0];
            uint32_t stride = subexpr->type->getStride();
            if ( index->rtti_isConstant() ) {
                // if its constant index, like a[3]..., we try to let node bellow simulate
                auto idxCE = static_pointer_cast(index);
                uint32_t idxC = cast::to(idxCE->value);
                if ( idxC >= range ) {
                    context.thisProgram->error("index out of range", "", "",
                        at, CompilationError::index_out_of_range);
                    return nullptr;
                }
                auto tnode = subexpr->trySimulate(context, extraOffset + idxC*stride, r2vType);
                if ( tnode ) {
                    return tnode;
                }
            }
            // regular scenario
            auto prv = subexpr->simulate(context);
            auto pidx = index->simulate(context);
            if ( r2vType->baseType!=Type::none ) {
                return context.code->makeValueNode(r2vType->baseType, at, prv, pidx, stride, extraOffset, range);
            } else {
                return context.code->makeNode(at, prv, pidx, stride, extraOffset, range);
            }
        }
    }

    SimNode * ExprAt::simulate (Context & context) const {
        if ( subexpr->type->isVectorType() ) {
            auto prv = subexpr->simulate(context);
            auto pidx = index->simulate(context);
            uint32_t range = subexpr->type->getVectorDim();
            uint32_t stride = type->getSizeOf();
            if ( subexpr->type->ref ) {
                auto res = context.code->makeNode(at, prv, pidx, stride, 0, range);
                if ( r2v ) {
                    return ExprRef2Value::GetR2V(context, at, type, res);
                } else {
                    return res;
                }
            } else {
                switch ( type->baseType ) {
                    case tInt:      return context.code->makeNode(at, prv, pidx, range);
                    case tUInt:
                    case tBitfield:
                                    return context.code->makeNode(at, prv, pidx, range);
                    case tFloat:    return context.code->makeNode(at, prv, pidx, range);
                    default:
                        DAS_ASSERTF(0, "we should not even be here. infer type should have failed on unsupported_vector[blah]");
                        context.thisProgram->error("internal compilation error, generating vector at for unsupported vector type.", "", "", at);
                        return nullptr;
                }
            }
        } else if ( subexpr->type->isGoodTableType() ) {
            auto prv = subexpr->simulate(context);
            auto pidx = index->simulate(context);
            uint32_t valueTypeSize = subexpr->type->secondType->getSizeOf();
            auto res = context.code->makeValueNode(subexpr->type->firstType->baseType, at, prv, pidx, valueTypeSize, 0);
            if ( r2v ) {
                return ExprRef2Value::GetR2V(context, at, type, res);
            } else {
                return res;
            }
        } else {
            if ( r2v ) {
                return trySimulate(context, 0, type);
            } else {
                return trySimulate(context, 0, make_smart(Type::none));
            }
        }
    }

    SimNode * ExprSafeAt::trySimulate (Context &, uint32_t, const TypeDeclPtr &) const {
        return nullptr;
    }

    SimNode * ExprSafeAt::simulate (Context & context) const {
        if ( subexpr->type->isPointer() ) {
            const auto & seT = subexpr->type->firstType;
            if ( seT->isGoodArrayType() ) {
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                uint32_t stride = seT->firstType->getSizeOf();
                return context.code->makeNode(at, prv, pidx, stride, 0);
            } else if ( seT->isGoodTableType() ) {
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                uint32_t valueTypeSize = seT->secondType->getSizeOf();
                return context.code->makeValueNode(seT->firstType->baseType, at, prv, pidx, valueTypeSize, 0);
            } else if ( seT->dim.size() ) {
                uint32_t range = seT->dim[0];
                uint32_t stride = seT->getStride();
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                return context.code->makeNode(at, prv, pidx, stride, 0, range);
            } else if ( seT->isVectorType() ) {
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                uint32_t range = seT->getVectorDim();
                uint32_t stride = type->getSizeOf();
                return context.code->makeNode(at, prv, pidx, stride, 0, range);
            } else {
                DAS_VERIFY(0 && "TODO: safe-at not implemented");
            }
        } else {
            const auto & seT = subexpr->type;
            if ( seT->isGoodArrayType() ) {
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                uint32_t stride = seT->firstType->getSizeOf();
                return context.code->makeNode(at, prv, pidx, stride, 0);
            } else if ( subexpr->type->isGoodTableType() ) {
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                uint32_t valueTypeSize = seT->secondType->getSizeOf();
                return context.code->makeValueNode(seT->firstType->baseType, at, prv, pidx, valueTypeSize, 0);
            } else if ( seT->dim.size() ) {
                uint32_t range = seT->dim[0];
                uint32_t stride = seT->getStride();
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                return context.code->makeNode(at, prv, pidx, stride, 0, range);
            } else if ( seT->isVectorType() && seT->ref ) {
                auto prv = subexpr->simulate(context);
                auto pidx = index->simulate(context);
                uint32_t range = seT->getVectorDim();
                uint32_t stride = type->getSizeOf();
                return context.code->makeNode(at, prv, pidx, stride, 0, range);
            } else {
                DAS_VERIFY(0 && "TODO: safe-at not implemented");
            }
        }
        return nullptr;
    }

    vector ExprBlock::collectExpressions ( Context & context,
                                                     const vector & lis,
                                                     das_map * ofsmap ) const {
        vector simlist;
        for ( auto & node : lis ) {
            if ( node->rtti_isLet()) {
                auto pLet = static_pointer_cast(node);
                auto letInit = ExprLet::simulateInit(context, pLet.get());
                simlist.insert(simlist.end(), letInit.begin(), letInit.end());
                continue;
            }
            if ( node->rtti_isLabel() ) {
                if ( ofsmap ) {
                    auto lnode = static_pointer_cast(node);
                    (*ofsmap)[lnode->label] = uint32_t(simlist.size());
                }
                continue;
            }
            if ( auto simE = node->simulate(context) ) {
                simlist.push_back(simE);
            }
        }
        return simlist;
    }

    void ExprBlock::simulateFinal ( Context & context, SimNode_Final * block ) const {
        vector simFList = collectExpressions(context, finalList);
        block->totalFinal = int(simFList.size());
        if ( block->totalFinal ) {
            block->finalList = (SimNode **) context.code->allocate(sizeof(SimNode *)*block->totalFinal);
            for ( uint32_t i = 0; i != block->totalFinal; ++i )
                block->finalList[i] = simFList[i];
        }
    }

    void ExprBlock::simulateBlock ( Context & context, SimNode_Block * block ) const {
        das_map ofsmap;
        vector simlist = collectExpressions(context, list, &ofsmap);
        block->total = int(simlist.size());
        if ( block->total ) {
            block->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*block->total);
            for ( uint32_t i = 0; i != block->total; ++i )
                block->list[i] = simlist[i];
        }
        simulateLabels(context, block, ofsmap);
    }

    void ExprBlock::simulateLabels ( Context & context, SimNode_Block * block, const das_map & ofsmap ) const {
        if ( maxLabelIndex!=-1 ) {
            block->totalLabels = maxLabelIndex + 1;
            block->labels = (uint32_t *) context.code->allocate(block->totalLabels * sizeof(uint32_t));
            for ( uint32_t i=0; i!=block->totalLabels; ++i ) {
                block->labels[i] = -1U;
            }
            for ( auto & it : ofsmap ) {
                block->labels[it.first] = it.second;
            }
        }
    }

    SimNode * ExprBlock::simulate (Context & context) const {
        das_map ofsmap;
        vector simlist = collectExpressions(context, list, &ofsmap);
        // wow, such empty
        if ( finalList.size()==0 && simlist.size()==0 && !annotationDataSid ) {
            return context.code->makeNode(at);
        }
        // we memzero block's stack memory, if there is a finally section
        // bad scenario we fight is ( in scope X ; return ; in scope Y )
        if ( finalList.size() ) {
            uint32_t blockDataSize = stackVarBottom - stackVarTop;
            if ( blockDataSize ) {
                for ( const auto & svr : stackCleanVars ) {
                    SimNode * fakeVar = context.code->makeNode(at, svr.first);
                    SimNode * memZ = context.code->makeNode(at, fakeVar, svr.second );
                    simlist.insert( simlist.begin(), memZ );
                }
            }
        }
        // TODO: what if list size is 0?
        if ( simlist.size()!=1 || isClosure || finalList.size() ) {
            SimNode_Block * block;
            if ( isClosure ) {
                bool needResult = type!=nullptr && type->baseType!=Type::tVoid;
                bool C0 = !needResult && simlist.size()==1 && finalList.size()==0;
#if DAS_DEBUGGER
                if ( context.thisProgram->getDebugger() ) {
                    block = context.code->makeNode(at, needResult, C0, annotationData);
                } else
#endif
                {
                    block = context.code->makeNode(at, needResult, C0, annotationData);
                }
            } else {
                if ( maxLabelIndex!=-1 ) {
#if DAS_DEBUGGER
                    if ( context.thisProgram->getDebugger() ) {
                        block = context.code->makeNode(at);
                    } else
#endif
                    {
                        block = context.code->makeNode(at);
                    }
                    simulateLabels(context, block, ofsmap);
                } else {
                    if ( finalList.size()==0 ) {
#if DAS_DEBUGGER
                        if ( context.thisProgram->getDebugger() ) {
                            block = context.code->makeNode(at);
                        } else
#endif
                        {
                            block = context.code->makeNode(at);
                        }
                    } else {
#if DAS_DEBUGGER
                        if ( context.thisProgram->getDebugger() ) {
                            block = context.code->makeNode(at);
                        } else
#endif
                        {
                            block = context.code->makeNode(at);
                        }
                    }
                }
            }
            block->annotationDataSid = annotationDataSid;
            block->total = int(simlist.size());
            if ( block->total ) {
                block->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*block->total);
                for ( uint32_t i = 0; i != block->total; ++i )
                    block->list[i] = simlist[i];
            }
            if ( !inTheLoop ) {
                simulateFinal(context, block);
            }
            return block;
        } else {
            return simlist[0];
        }
    }

    SimNode * ExprSwizzle::trySimulate (Context & context, uint32_t extraOffset, const TypeDeclPtr & r2vType ) const {
        if ( !value->type->ref ) {
            return nullptr;
        }
        uint32_t offset = fields[0] * sizeof(float);
        if ( auto chain = value->trySimulate(context, extraOffset + offset, r2vType) ) {
            return chain;
        }
        auto simV = value->simulate(context);
        if ( r2vType->baseType!=Type::none ) {
            return context.code->makeValueNode(r2vType->baseType,at,simV,offset + extraOffset);
        } else {
            return context.code->makeNode(at,simV,offset + extraOffset);
        }
    }

    SimNode * ExprSwizzle::simulate (Context & context) const {
        if ( !type->ref ) {
            bool seq = TypeDecl::isSequencialMask(fields);
            if (seq && value->type->ref) {
                return trySimulate(context, 0, type);
            } else {
                auto fsz = fields.size();
                uint8_t fs[4];
                fs[0] = fields[0];
                fs[1] = fsz >= 2 ? fields[1] : fields[0];
                fs[2] = fsz >= 3 ? fields[2] : fields[0];
                fs[3] = fsz >= 4 ? fields[3] : fields[0];
                auto simV = value->simulate(context);
                return context.code->makeNode(at, simV, fs);
            }
        } else {
            return trySimulate(context, 0, r2v ? type : make_smart(Type::none));
        }
    }

    SimNode * ExprField::simulate (Context & context) const {
        if ( value->type->isBitfield() ) {
            auto simV = value->simulate(context);
            uint32_t mask = 1u makeNode(at, simV, mask);
        } else if ( !field  && fieldIndex==-1 ) {
            if ( r2v ) {
                auto resN = annotation->simulateGetFieldR2V(name, context, at, value);
                if ( !resN ) {
                    context.thisProgram->error("integration error, simulateGetFieldR2V returned null", "", "",
                                               at, CompilationError::missing_node );
                }
                return resN;
            } else {
                auto resN = annotation->simulateGetField(name, context, at, value);
                if ( !resN ) {
                    context.thisProgram->error("integration error, simulateGetField returned null", "", "",
                                               at, CompilationError::missing_node );
                }
                return resN;
            }
        } else {
            return trySimulate(context, 0, r2v ? type : make_smart(Type::none));
        }
    }

    SimNode * ExprField::trySimulate (Context & context, uint32_t extraOffset, const TypeDeclPtr & r2vType ) const {
        if ( !field && fieldIndex==-1 ) {
            return nullptr;
        }
        if ( value->type->isBitfield() ) {
            return nullptr;
        }
        int fieldOffset = -1;
        if ( fieldIndex != - 1 ) {
            if ( value->type->isPointer() ) {
                if ( value->type->firstType->isVariant() ) {
                    fieldOffset = value->type->firstType->getVariantFieldOffset(fieldIndex);
                } else {
                    fieldOffset = value->type->firstType->getTupleFieldOffset(fieldIndex);
                }
            } else {
                if ( value->type->isVariant() ) {
                    fieldOffset = value->type->getVariantFieldOffset(fieldIndex);
                } else {
                    fieldOffset = value->type->getTupleFieldOffset(fieldIndex);
                }
            }
        } else {
            DAS_ASSERTF(field, "field can't be null");
            if (!field) return nullptr;
            fieldOffset = field->offset;
        }
        DAS_ASSERTF(fieldOffset>=0,"field offset is somehow not there");
        if (value->type->isPointer()) {
            if ( unsafeDeref ) {
                auto simV = value->simulate(context);
                if ( r2vType->baseType!=Type::none ) {
                    return context.code->makeValueNode(r2vType->baseType, at, simV, fieldOffset + extraOffset);
                }
                else {
                    return context.code->makeNode(at, simV, fieldOffset + extraOffset);
                }
            } else {
                auto simV = value->simulate(context);
                if ( r2vType->baseType!=Type::none ) {
                    return context.code->makeValueNode(r2vType->baseType, at, simV, fieldOffset + extraOffset);
                }
                else {
                    return context.code->makeNode(at, simV, fieldOffset + extraOffset);
                }
            }
        } else {
            if ( auto chain = value->trySimulate(context, extraOffset + fieldOffset, r2vType) ) {
                return chain;
            }
            auto simV = value->simulate(context);
            if ( r2vType->baseType!=Type::none ) {
                return context.code->makeValueNode(r2vType->baseType, at, simV, extraOffset + fieldOffset);
            } else {
                return context.code->makeNode(at, simV, extraOffset + fieldOffset);
            }
        }
    }

    SimNode * ExprIsVariant::simulate(Context & context) const {
        DAS_ASSERT(fieldIndex != -1);
        return context.code->makeNode(at, value->simulate(context), fieldIndex);
    }

    SimNode * ExprAsVariant::simulate (Context & context) const {
        int fieldOffset = value->type->getVariantFieldOffset(fieldIndex);
        auto simV = value->simulate(context);
        if ( r2v ) {
            return context.code->makeValueNode(type->baseType, at, simV, fieldOffset, fieldIndex);
        } else {
            return context.code->makeNode(at, simV, fieldOffset, fieldIndex);
        }
    }

    SimNode * ExprSafeAsVariant::simulate (Context & context) const {
        int fieldOffset = value->type->isPointer() ?
            value->type->firstType->getVariantFieldOffset(fieldIndex) :
            value->type->getVariantFieldOffset(fieldIndex);
        auto simV = value->simulate(context);
        if ( skipQQ ) {
            return context.code->makeNode(at,simV,fieldOffset, fieldIndex);
        } else {
            return context.code->makeNode(at,simV,fieldOffset, fieldIndex);
        }
    }

    SimNode * ExprSafeField::trySimulate(Context &, uint32_t, const TypeDeclPtr &) const {
        return nullptr;
    }

    SimNode * ExprSafeField::simulate (Context & context) const {
        int fieldOffset = -1;
        if ( !annotation ) {
            if ( fieldIndex != - 1 ) {
                if ( value->type->firstType->isVariant() ) {
                    fieldOffset = value->type->firstType->getVariantFieldOffset(fieldIndex);
                } else {
                    fieldOffset = value->type->firstType->getTupleFieldOffset(fieldIndex);
                }
            } else {
                fieldOffset = field->offset;
            }
            DAS_ASSERTF(fieldOffset>=0,"field offset is somehow not there");
        }
        if ( skipQQ ) {
            if ( annotation ) {
                auto resN = annotation->simulateSafeGetFieldPtr(name, context, at, value);
                if ( !resN ) {
                    context.thisProgram->error("integration error, simulateSafeGetFieldPtr returned null", "", "",
                                               at, CompilationError::missing_node );
                }
                return resN;
            } else {
                return context.code->makeNode(at,value->simulate(context),fieldOffset);
            }
        } else {
            if ( annotation ) {
                auto resN = annotation->simulateSafeGetField(name, context, at, value);
                if ( !resN ) {
                    context.thisProgram->error("integration error, simulateSafeGetField returned null", "", "",
                                               at, CompilationError::missing_node );
                }
                return resN;
            } else {
                return context.code->makeNode(at,value->simulate(context),fieldOffset);
            }
        }
    }

    SimNode * ExprStringBuilder::simulate (Context & context) const {
        SimNode_StringBuilder * pSB = context.code->makeNode(at);
        if ( int nArg = (int) elements.size() ) {
            pSB->arguments = (SimNode **) context.code->allocate(nArg * sizeof(SimNode *));
            pSB->types = (TypeInfo **) context.code->allocate(nArg * sizeof(TypeInfo *));
            pSB->nArguments = nArg;
            for ( int a=0; a!=nArg; ++a ) {
                pSB->arguments[a] = elements[a]->simulate(context);
                pSB->types[a] = context.thisHelper->makeTypeInfo(nullptr, elements[a]->type);
            }
        } else {
            pSB->arguments = nullptr;
            pSB->types = nullptr;
            pSB->nArguments = 0;
        }
        return pSB;
    }

    SimNode * ExprVar::trySimulate (Context & context, uint32_t extraOffset, const TypeDeclPtr & r2vType ) const {
        if ( block ) {
        } else if ( local ) {
            if ( variable->type->ref ) {
                if ( r2vType->baseType!=Type::none ) {
                    return context.code->makeValueNode(r2vType->baseType, at,
                                                    variable->stackTop, extraOffset);
                } else {
                    return context.code->makeNode(at,
                                                    variable->stackTop, extraOffset);
                }
            } else if ( variable->aliasCMRES ) {
                if ( r2vType->baseType!=Type::none ) {
                    return context.code->makeValueNode(r2vType->baseType, at,extraOffset);
                } else {
                    return context.code->makeNode(at, extraOffset);
                }
            } else {
                if ( r2vType->baseType!=Type::none ) {
                    return context.code->makeValueNode(r2vType->baseType, at,
                                                                            variable->stackTop + extraOffset);
                } else {
                    return context.code->makeNode(at, variable->stackTop + extraOffset);
                }
            }
        } else if ( argument ) {
            if ( variable->type->isPointer() && variable->type->isRef() ) {
                return nullptr;
            } else if ( variable->type->isPointer() ) {
                if ( r2vType->baseType!=Type::none ) {
                    return context.code->makeValueNode(r2vType->baseType, at, argumentIndex, extraOffset);
                } else {
                    return context.code->makeNode(at, argumentIndex, extraOffset);
                }
            } else if (variable->type->isRef()) {
                if ( r2vType->baseType!=Type::none ) {
                    return context.code->makeValueNode(r2vType->baseType, at, argumentIndex, extraOffset);
                } else {
                    return context.code->makeNode(at, argumentIndex, extraOffset);
                }
            }
        } else { // global

        }
        return nullptr;
    }

    SimNode * ExprVar::simulate (Context & context) const {
        if ( block ) {
            auto blk = pBlock;
            if (variable->type->isRef()) {
                if (r2v && !type->isRefType()) {
                    if ( thisBlock ) {
                        return context.code->makeValueNode(type->baseType, at, argumentIndex);
                    } else {
                        return context.code->makeValueNode(type->baseType, at, argumentIndex, blk->stackTop);
                    }
                } else {
                    if ( thisBlock ) {
                        return context.code->makeNode(at, argumentIndex);
                    } else {
                        return context.code->makeNode(at, argumentIndex, blk->stackTop);
                    }
                }
            } else {
                if (r2v && !type->isRefType()) {
                    if ( thisBlock ) {
                        return context.code->makeNode(at, argumentIndex);
                    } else {
                        return context.code->makeNode(at, argumentIndex, blk->stackTop);
                    }
                }
                else {
                    if ( thisBlock ) {
                        return context.code->makeNode(at, argumentIndex);
                    } else {
                        return context.code->makeNode(at, argumentIndex, blk->stackTop);
                    }
                }
            }
        } else if ( local ) {
            if ( r2v ) {
                return trySimulate(context, 0, type);
            } else {
                return trySimulate(context, 0, make_smart(Type::none));
            }
        } else if ( argument) {
            if (variable->type->isRef()) {
                if (r2v && !type->isRefType()) {
                    return context.code->makeValueNode(type->baseType, at, argumentIndex);
                } else {
                    return context.code->makeNode(at, argumentIndex);
                }
            } else {
                if (r2v && !type->isRefType()) {
                    return context.code->makeNode(at, argumentIndex);
                }
                else {
                    return context.code->makeNode(at, argumentIndex);
                }
            }
        } else {
            DAS_ASSERT(variable->index >= 0 && "using variable which is not used. how?");
            uint64_t mnh = variable->getMangledNameHash();
            if ( !variable->module->isSolidContext ) {
                if ( variable->global_shared ) {
                    if ( r2v ) {
                        return context.code->makeValueNode(type->baseType, at, variable->stackTop, mnh);
                    } else {
                        return context.code->makeNode(at, variable->stackTop, mnh);
                    }
                } else {
                    if ( r2v ) {
                        return context.code->makeValueNode(type->baseType, at, variable->stackTop, mnh);
                    } else {
                        return context.code->makeNode(at, variable->stackTop, mnh);
                    }
                }
            } else {
                if ( variable->global_shared ) {
                    if ( r2v ) {
                        return context.code->makeValueNode(type->baseType, at, variable->stackTop, mnh);
                    } else {
                        return context.code->makeNode(at, variable->stackTop, mnh);
                    }
                } else {
                    if ( r2v ) {
                        return context.code->makeValueNode(type->baseType, at, variable->stackTop, mnh);
                    } else {
                        return context.code->makeNode(at, variable->stackTop, mnh);
                    }
                }
            }
        }
    }

    SimNode * ExprOp1::simulate (Context & context) const {
        vector sarguments = { subexpr };
        if ( func->builtIn && !func->callBased ) {
            auto pSimOp1 = static_cast(func->makeSimNode(context,sarguments));
            pSimOp1->debugInfo = at;
            pSimOp1->x = subexpr->simulate(context);
            return pSimOp1;
        } else {
            auto pCall = static_cast(func->makeSimNode(context,sarguments));
            pCall->debugInfo = at;
            pCall->fnPtr = context.getFunction(func->index);
            pCall->arguments = (SimNode **) context.code->allocate(1 * sizeof(SimNode *));
            pCall->nArguments = 1;
            pCall->arguments[0] = subexpr->simulate(context);
            pCall->cmresEval = context.code->makeNode(at,stackTop);
            return pCall;
        }
    }

    SimNode * ExprOp2::simulate (Context & context) const {
        vector sarguments = { left, right };
        if ( func->builtIn && !func->callBased ) {
            auto pSimOp2 = static_cast(func->makeSimNode(context,sarguments));
            pSimOp2->debugInfo = at;
            pSimOp2->l = left->simulate(context);
            pSimOp2->r = right->simulate(context);
            return pSimOp2;
        } else {
            auto pCall = static_cast(func->makeSimNode(context,sarguments));
            pCall->debugInfo = at;
            pCall->fnPtr = context.getFunction(func->index);
            pCall->arguments = (SimNode **) context.code->allocate(2 * sizeof(SimNode *));
            pCall->nArguments = 2;
            pCall->arguments[0] = left->simulate(context);
            pCall->arguments[1] = right->simulate(context);
            pCall->cmresEval = context.code->makeNode(at,stackTop);
            return pCall;
        }
    }

    SimNode * ExprOp3::simulate (Context & context) const {
        return context.code->makeNode(at,
                                                    subexpr->simulate(context),
                                                    left->simulate(context),
                                                    right->simulate(context));
    }

    SimNode * ExprMove::simulate (Context & context) const {
        auto retN = makeMove(at,context,left,right);
        if ( !retN ) {
            context.thisProgram->error("internal compilation error, can't generate move", "", "", at);
        }
        return retN;
    }

    SimNode * ExprClone::simulate (Context & context) const {
        SimNode * retN = nullptr;
        if ( left->type->isHandle() ) {
            auto lN = left->simulate(context);
            auto rN = right->simulate(context);
            retN = left->type->annotation->simulateClone(context, at, lN, rN);
        } else if ( left->type->canCopy() ) {
            retN = makeCopy(at, context, left, right );
        } else {
            retN = nullptr;
        }
        if ( !retN ) {
            context.thisProgram->error("internal compilation error, can't generate clone", "", "", at);
        }
        return retN;
    }


    SimNode * ExprCopy::simulate (Context & context) const {
        if ( takeOverRightStack ) {
            auto sl = left->simulate(context);
            auto sr = right->simulate(context);
            return context.code->makeNode(at, sl, sr, stackTop);
        } else {
            auto retN = makeCopy(at, context, left, right);
            if ( !retN ) {
                context.thisProgram->error("internal compilation error, can't generate copy", "", "", at);
            }
            return retN;
        }
    }

    SimNode * ExprTryCatch::simulate (Context & context) const {
#if DAS_DEBUGGER
        if ( context.thisProgram->getDebugger() ) {
            return context.code->makeNode(at,
                                                    try_block->simulate(context),
                                                    catch_block->simulate(context));
        } else
#endif
        {
            return context.code->makeNode(at,
                                                    try_block->simulate(context),
                                                    catch_block->simulate(context));
        }
    }

    SimNode * ExprReturn::simulate (Context & context) const {
        // return string is its own thing
        if (subexpr && subexpr->type && subexpr->rtti_isConstant()) {
            if (subexpr->type->isSimpleType(Type::tString)) {
                auto cVal = static_pointer_cast(subexpr);
                char * str = context.constStringHeap->allocateString(cVal->text);
                return context.code->makeNode(at, str);
            }
        }
        // now, lets do the standard everything
        bool skipIt = false;
        if ( subexpr && subexpr->rtti_isMakeLocal() ) {
            if ( static_pointer_cast(subexpr)->useCMRES ) {
                skipIt = true;
            }
        }
        SimNode * simSubE = (subexpr && !skipIt) ? subexpr->simulate(context) : nullptr;
        if (!subexpr) {
            return context.code->makeNode(at);
        } else if ( subexpr->rtti_isConstant() ) {
            auto cVal = static_pointer_cast(subexpr);
            return context.code->makeNode(at, cVal->value);
        }
        if ( returnReference ) {
            if ( returnInBlock ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at, simSubE);
            } else {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at, simSubE);
            }
        } else if ( returnInBlock ) {
            if ( returnCallCMRES ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                SimNode_CallBase * simRet = (SimNode_CallBase *) simSubE;
                simRet->cmresEval = context.code->makeNode(at,0,stackTop);
                return context.code->makeNode(at, simSubE);
            } else if ( takeOverRightStack ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at,
                            simSubE, refStackTop, stackTop);
            } else if ( block->copyOnReturn  ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at,
                            simSubE, subexpr->type->getSizeOf(), stackTop);
            } else if ( block->moveOnReturn ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at,
                    simSubE, subexpr->type->getSizeOf(), stackTop);
            }
        } else if ( subexpr ) {
            if ( returnCallCMRES ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                SimNode_CallBase * simRet = (SimNode_CallBase *) simSubE;
                simRet->cmresEval = context.code->makeNode(at,0);
                return context.code->makeNode(at, simSubE);
            } else if ( returnCMRES ) {
                // ReturnLocalCMRes
                if ( subexpr->rtti_isMakeLocal() ) {
                    auto mkl = static_pointer_cast(subexpr);
                    if ( mkl->useCMRES ) {
                        SimNode_Block * blockT = context.code->makeNode(at);
                        auto simlist = mkl->simulateLocal(context);
                        blockT->total = int(simlist.size());
                        blockT->list = (SimNode **) context.code->allocate(sizeof(SimNode *)*blockT->total);
                        for ( uint32_t i = 0; i != blockT->total; ++i )
                            blockT->list[i] = simlist[i];
                        return blockT;
                    }
                }
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at, simSubE);
            } else if ( takeOverRightStack ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at, simSubE, refStackTop);
            } else if ( returnFunc && returnFunc->copyOnReturn ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at, simSubE, subexpr->type->getSizeOf());
            } else if ( returnFunc && returnFunc->moveOnReturn ) {
                DAS_ASSERTF(simSubE, "internal error. can't be zero");
                return context.code->makeNode(at, simSubE, subexpr->type->getSizeOf());
            }
        }
        DAS_ASSERTF(simSubE, "internal error. can't be zero");
        if ( moveSemantics ) {
            // TODO: support by-value annotations?
            if ( subexpr->type->isRef() ) {
                return context.code->makeValueNode(subexpr->type->baseType, at, simSubE);
            } else {
                return context.code->makeNode(at, simSubE);
            }
        } else {
            return context.code->makeNode(at, simSubE);
        }
    }

    SimNode * ExprBreak::simulate (Context & context) const {
        return context.code->makeNode(at);
    }

    SimNode * ExprContinue::simulate (Context & context) const {
        return context.code->makeNode(at);
    }

    SimNode * ExprIfThenElse::simulate (Context & context) const {
        ExpressionPtr zeroCond;
        bool condIfZero = false;
        bool match0 = matchEquNequZero(cond, zeroCond, condIfZero);
#if DAS_DEBUGGER
        if ( context.thisProgram->getDebugger() ) {
            if ( match0 && zeroCond->type->isWorkhorseType() ) {
                if ( condIfZero ) {
                    if ( if_false ) {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context),
                                                if_false->simulate(context));

                    } else {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context));
                    }
                } else {
                    if ( if_false ) {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context),
                                                if_false->simulate(context));
                    } else {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context));
                    }
                }
            } else {
                // good old if
                if ( if_false ) {
                    return context.code->makeNode(at, cond->simulate(context),
                                        if_true->simulate(context), if_false->simulate(context));
                } else {
                    return context.code->makeNode(at, cond->simulate(context),
                                        if_true->simulate(context));
                }
            }
        } else
#endif
        {
            if ( match0 && zeroCond->type->isWorkhorseType() ) {
                if ( condIfZero ) {
                    if ( if_false ) {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context),
                                                if_false->simulate(context));

                    } else {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context));
                    }
                } else {
                    if ( if_false ) {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context),
                                                if_false->simulate(context));
                    } else {
                        return context.code->makeNumericValueNode(zeroCond->type->baseType,
                                        at, zeroCond->simulate(context), if_true->simulate(context));
                    }
                }
            } else {
                // good old if
                if ( if_false ) {
                    return context.code->makeNode(at, cond->simulate(context),
                                        if_true->simulate(context), if_false->simulate(context));
                } else {
                    return context.code->makeNode(at, cond->simulate(context),
                                        if_true->simulate(context));
                }
            }
        }
    }

    SimNode * ExprWith::simulate (Context & context) const {
        return body->simulate(context);
    }

    SimNode * ExprAssume::simulate (Context &) const {
        return nullptr;
    }

    void ExprWhile::simulateFinal ( Context & context, const ExpressionPtr & bod, SimNode_Block * blk ) {
        if ( bod->rtti_isBlock() ) {
            auto pBlock = static_pointer_cast(bod);
            pBlock->simulateBlock(context, blk);
            pBlock->simulateFinal(context, blk);
        } else {
            context.thisProgram->error("internal error, expecting block", "", "", bod->at);
        }
    }

    SimNode * ExprWhile::simulate (Context & context) const {
#if DAS_DEBUGGER
        if ( context.thisProgram->getDebugger() ) {
            auto node = context.code->makeNode(at, cond->simulate(context));
            simulateFinal(context, body, node);
            return node;
        } else
#endif
        {
            auto node = context.code->makeNode(at, cond->simulate(context));
            simulateFinal(context, body, node);
            return node;
        }
    }

    SimNode * ExprUnsafe::simulate (Context & context) const {
        return body->simulate(context);
    }

    SimNode * ExprFor::simulate (Context & context) const {
        // determine iteration types
        bool nativeIterators = false;
        bool fixedArrays = false;
        bool dynamicArrays = false;
        bool stringChars = false;
        bool rangeBase = false;
        int32_t fixedSize = INT32_MAX;
        for ( auto & src : sources ) {
            if ( !src->type ) continue;
            if ( src->type->isArray() ) {
                fixedSize = das::min(fixedSize, src->type->dim[0]);
                fixedArrays = true;
            } else if ( src->type->isGoodArrayType() ) {
                dynamicArrays = true;
            } else if ( src->type->isGoodIteratorType() ) {
                nativeIterators = true;
            } else if ( src->type->isHandle() ) {
                nativeIterators = true;
            } else if ( src->type->isRange() ) {
                rangeBase = true;
            } else if ( src->type->isString() ) {
                stringChars = true;
            }
        }
        // create loops based on
        int  total = int(sources.size());
        int  sourceTypes = int(dynamicArrays) + int(fixedArrays) + int(rangeBase) + int(stringChars);
        bool hybridRange = rangeBase && (total>1);
        if ( (sourceTypes>1) || hybridRange || nativeIterators || stringChars || /* this is how much we can unroll */ total>MAX_FOR_UNROLL ) {
            SimNode_ForWithIteratorBase * result;
#if DAS_DEBUGGER
            if ( context.thisProgram->getDebugger() ) {
                if ( total>MAX_FOR_UNROLL ) {
                    result = (SimNode_ForWithIteratorBase *) context.code->makeNode(at);
                } else {
                    result = (SimNode_ForWithIteratorBase *) context.code->makeNodeUnrollNZ_FOR(total, at);
                }
            } else
#endif
            {
                if ( total>MAX_FOR_UNROLL ) {
                    result = (SimNode_ForWithIteratorBase *) context.code->makeNode(at);
                } else {
                    result = (SimNode_ForWithIteratorBase *) context.code->makeNodeUnrollNZ_FOR(total, at);
                }
            }
            result->allocateFor(context.code.get(), total);
            for ( int t=0; t!=total; ++t ) {
                if ( sources[t]->type->isGoodIteratorType() ) {
                    result->source_iterators[t] = context.code->makeNode(
                        sources[t]->at,
                        sources[t]->simulate(context));
                } else if ( sources[t]->type->isGoodArrayType() ) {
                    result->source_iterators[t] = context.code->makeNode(
                        sources[t]->at,
                        sources[t]->simulate(context),
                        sources[t]->type->firstType->getSizeOf());
                } else if ( sources[t]->type->isRange() ) {
                    result->source_iterators[t] = context.code->makeNode(
                        sources[t]->at,
                        sources[t]->simulate(context),
                        sources[t]->type->baseType==Type::tRange);
                } else if ( sources[t]->type->isString() ) {
                    result->source_iterators[t] = context.code->makeNode(
                        sources[t]->at,
                        sources[t]->simulate(context));
                } else if ( sources[t]->type->isHandle() ) {
                    if ( !result ) {
                        context.thisProgram->error("integration error, simulateGetIterator returned null", "", "",
                                                   at, CompilationError::missing_node );
                        return nullptr;
                    } else {
                        result->source_iterators[t] = sources[t]->type->annotation->simulateGetIterator(
                            context,
                            sources[t]->at,
                            sources[t]
                        );
                    }
                } else if ( sources[t]->type->dim.size() ) {
                    result->source_iterators[t] = context.code->makeNode(
                        sources[t]->at,
                        sources[t]->simulate(context),
                        sources[t]->type->dim[0],
                        sources[t]->type->getStride());
                } else {
                    DAS_ASSERTF(0, "we should not be here. we are doing iterator for on an unsupported type.");
                    context.thisProgram->error("internal compilation error, generating for-with-iterator", "", "", at);
                    return nullptr;
                }
                result->stackTop[t] = iteratorVariables[t]->stackTop;
            }
            ExprWhile::simulateFinal(context, body, result);
            return result;
        } else {
            auto flagsE = body->getEvalFlags();
            bool NF = flagsE == 0;
            SimNode_ForBase * result;
            DAS_ASSERT(body->rtti_isBlock() && "there would be internal error otherwise");
            auto subB = static_pointer_cast(body);
            bool loop1 = (subB->list.size() == 1);
#if DAS_DEBUGGER
            if ( context.thisProgram->getDebugger() ) {
                if ( dynamicArrays ) {
                    if (loop1) {
                        result = (SimNode_ForBase *) context.code->makeNodeUnrollNZ_FOR(total, at);
                    } else {
                        result = (SimNode_ForBase *) context.code->makeNodeUnrollNZ_FOR(total, at);
                    }
                } else if ( fixedArrays ) {
                    if (loop1) {
                        result = (SimNode_ForBase *)context.code->makeNodeUnrollNZ_FOR(total, at);
                    } else {
                        result = (SimNode_ForBase *)context.code->makeNodeUnrollNZ_FOR(total, at);
                    }
                } else if ( rangeBase ) {
                    DAS_ASSERT(total==1 && "simple range on 1 loop only");
                    bool isSigned = sources[0]->type->baseType == Type::tRange;
                    if ( NF ) {
                        if (loop1) {
                            result = context.code->makeNode(at,isSigned);
                        } else {
                            result = context.code->makeNode(at,isSigned);
                        }
                    } else {
                        if (loop1) {
                            result = context.code->makeNode(at,isSigned);
                        } else {
                            result = context.code->makeNode(at,isSigned);
                        }
                    }
                } else {
                    DAS_ASSERTF(0, "we should not be here yet. logic above assumes optimized for path of some kind.");
                    context.thisProgram->error("internal compilation error, generating for", "", "", at);
                    return nullptr;
                }
            } else
#endif
            {
                if ( dynamicArrays ) {
                    if (loop1) {
                        result = (SimNode_ForBase *) context.code->makeNodeUnrollNZ_FOR(total, at);
                    } else {
                        result = (SimNode_ForBase *) context.code->makeNodeUnrollNZ_FOR(total, at);
                    }
                } else if ( fixedArrays ) {
                    if (loop1) {
                        result = (SimNode_ForBase *)context.code->makeNodeUnrollNZ_FOR(total, at);
                    } else {
                        result = (SimNode_ForBase *)context.code->makeNodeUnrollNZ_FOR(total, at);
                    }
                } else if ( rangeBase ) {
                    DAS_ASSERT(total==1 && "simple range on 1 loop only");
                    bool isSigned = sources[0]->type->baseType == Type::tRange;
                    if ( NF ) {
                        if (loop1) {
                            result = context.code->makeNode(at,isSigned);
                        } else {
                            result = context.code->makeNode(at,isSigned);
                        }
                    } else {
                        if (loop1) {
                            result = context.code->makeNode(at,isSigned);
                        } else {
                            result = context.code->makeNode(at,isSigned);
                        }
                    }
                } else {
                    DAS_ASSERTF(0, "we should not be here yet. logic above assumes optimized for path of some kind.");
                    context.thisProgram->error("internal compilation error, generating for", "", "", at);
                    return nullptr;
                }
            }
            result->allocateFor(context.code.get(), total);
            for ( int t=0; t!=total; ++t ) {
                result->sources[t] = sources[t]->simulate(context);
                if ( sources[t]->type->isGoodArrayType() ) {
                    result->strides[t] = sources[t]->type->firstType->getSizeOf();
                } else {
                    result->strides[t] = sources[t]->type->getStride();
                }
                result->stackTop[t] = iteratorVariables[t]->stackTop;
            }
            result->size = fixedSize;
            ExprWhile::simulateFinal(context, body, result);
            return result;
        }
    }

    vector ExprLet::simulateInit(Context & context, const ExprLet * pLet) {
        vector simlist;
        simlist.reserve(pLet->variables.size());
        for (auto & var : pLet->variables) {
            SimNode * init;
            if (var->init) {
                init = ExprLet::simulateInit(context, var, true);
            } else if (var->aliasCMRES ) {
                int bytes = var->type->getSizeOf();
                if ( bytes makeNodeUnrollNZ(bytes, pLet->at,0);
                } else {
                    init = context.code->makeNode(pLet->at,0,bytes);
                }
            } else {
                init = context.code->makeNode(pLet->at, var->stackTop, var->type->getSizeOf());
            }
            if (init)
                simlist.push_back(init);
        }
        return simlist;
    }

    SimNode * ExprLet::simulateInit(Context & context, const VariablePtr & var, bool local) {
        SimNode * get;
        if ( local ) {
            if ( var->init && var->init->rtti_isMakeLocal() ) {
                return var->init->simulate(context);
            } else {
                get = context.code->makeNode(var->init->at, var->stackTop);
            }
        } else {
            if ( var->init && var->init->rtti_isMakeLocal() ) {
                return var->init->simulate(context);
            } else {
                if ( !var->module->isSolidContext ) {
                    if ( var->global_shared ) {
                        get = context.code->makeNode(var->init->at, var->index, var->getMangledNameHash());
                    } else {
                        get = context.code->makeNode(var->init->at, var->index, var->getMangledNameHash());
                    }
                } else {
                    if ( var->global_shared ) {
                        get = context.code->makeNode(var->init->at, var->index, var->getMangledNameHash());
                    } else {
                        get = context.code->makeNode(var->init->at, var->index, var->getMangledNameHash());
                    }
                }
            }
        }
        if ( var->type->ref ) {
            return context.code->makeNode(var->init->at, get,
                                                                 var->init->simulate(context));
        } else if ( var->init_via_move && var->type->canMove() ) {
            auto varExpr = make_smart(var->at, var->name);
            varExpr->variable = var;
            varExpr->local = local;
            varExpr->type = make_smart(*var->type);
            auto retN = makeMove(var->init->at, context, varExpr, var->init);
            if ( !retN ) {
                context.thisProgram->error("internal compilation error, can't generate move", "", "", var->at);
            }
            return retN;
        } else if ( !var->init_via_move && var->type->canCopy() ) {
            auto varExpr = make_smart(var->at, var->name);
            varExpr->variable = var;
            varExpr->local = local;
            varExpr->type = make_smart(*var->type);
            auto retN = makeCopy(var->init->at, context, varExpr, var->init);
            if ( !retN ) {
                context.thisProgram->error("internal compilation error, can't generate copy", "", "", var->at);
            }
            return retN;
        } else if ( var->isCtorInitialized() ) {
            auto varExpr = make_smart(var->at, var->name);
            varExpr->variable = var;
            varExpr->local = local;
            varExpr->type = make_smart(*var->type);
            SimNode_CallBase * retN = nullptr; // it has to be CALL with CMRES
            const auto & rE = var->init;
            if ( rE->rtti_isCall() ) {
                auto cll = static_pointer_cast(rE);
                if ( cll->func->copyOnReturn || cll->func->moveOnReturn ) {
                    retN = (SimNode_CallBase *) rE->simulate(context);
                    retN->cmresEval = varExpr->simulate(context);
                }
            }
            if ( !retN ) {
                context.thisProgram->error("internal compilation error, can't generate class constructor", "", "", var->at);
            }
            return retN;
        } else {
            context.thisProgram->error("internal compilation error, initializing variable which can't be copied or moved", "", "", var->at);
            return nullptr;
        }
    }

    SimNode * ExprLet::simulate (Context & context) const {
        auto let = context.code->makeNode(at);
        let->total = (uint32_t) variables.size();
        let->list = (SimNode **) context.code->allocate(let->total * sizeof(SimNode*));
        auto simList = ExprLet::simulateInit(context, this);
        copy(simList.data(), simList.data() + simList.size(), let->list);
        return let;
    }

    SimNode_CallBase * ExprCall::simulateCall (const FunctionPtr & func,
                                               const ExprLooksLikeCall * expr,
                                               Context & context,
                                               SimNode_CallBase * pCall) {
        bool needTypeInfo = false;
        for ( auto & arg : func->arguments ) {
            if ( arg->type->baseType==Type::anyArgument )
                needTypeInfo = true;
        }
        pCall->debugInfo = expr->at;
        if ( func->builtIn) {
            pCall->fnPtr = nullptr;
        } else if ( func->index>=0 ) {
            pCall->fnPtr = context.getFunction(func->index);
            DAS_ASSERTF(pCall->fnPtr, "calling function which null. how?");
        } else {
            DAS_ASSERTF(0, "calling function which is not used. how?");
        }
        if ( int nArg = (int) expr->arguments.size() ) {
            pCall->arguments = (SimNode **) context.code->allocate(nArg * sizeof(SimNode *));
            if ( needTypeInfo ) {
                pCall->types = (TypeInfo **) context.code->allocate(nArg * sizeof(TypeInfo *));
            } else {
                pCall->types = nullptr;
            }
            pCall->nArguments = nArg;
            for ( int a=0; a!=nArg; ++a ) {
                pCall->arguments[a] = expr->arguments[a]->simulate(context);
                if ( pCall->types ) {
                    if ( func->arguments[a]->type->baseType==Type::anyArgument ) {
                        pCall->types[a] = context.thisHelper->makeTypeInfo(nullptr, expr->arguments[a]->type);
                    } else {
                        pCall->types[a] = nullptr;
                    }
                }
            }
        } else {
            pCall->arguments = nullptr;
            pCall->nArguments = 0;
        }
        return pCall;

    }

    SimNode * ExprCall::simulate (Context & context) const {
        auto pCall = static_cast(func->makeSimNode(context,arguments));
        simulateCall(func, this, context, pCall);
        if ( !doesNotNeedSp && stackTop ) {
            pCall->cmresEval = context.code->makeNode(at,stackTop);
        }
        return pCall;
    }

    SimNode * ExprNamedCall::simulate (Context &) const {
        DAS_ASSERTF(false, "we should not be here. named call should be promoted to regular call");
        return nullptr;
    }

    void Program::buildGMNLookup ( Context & context, TextWriter & logs ) {
        context.tabGMnLookup.clear();
        for ( int i=0; i!=context.totalVariables; ++i ) {
            auto mnh = context.globalVariables[i].mangledNameHash;
            context.tabGMnLookup[mnh] = context.globalVariables[i].offset;
        }
        if ( options.getBoolOption("log_gmn_hash",false) ) {
            logs
                name);
                    gvar.size = pvar->type->getSizeOf();
                    gvar.debugInfo = helper.makeVariableDebugInfo(*pvar);
                    gvar.flags = 0;
                    if ( pvar->global_shared ) {
                        gvar.offset = pvar->stackTop = context.sharedSize;
                        gvar.shared = true;
                        context.sharedSize = (context.sharedSize + gvar.size + 0xf) & ~0xf;
                    } else {
                        gvar.offset = pvar->stackTop = context.globalsSize;
                        context.globalsSize = (context.globalsSize + gvar.size + 0xf) & ~0xf;
                    }
                    gvar.mangledNameHash = pvar->getMangledNameHash();
                });
            }
        }
        context.globals = (char *) das_aligned_alloc16(context.globalsSize);
        context.shared = (char *) das_aligned_alloc16(context.sharedSize);
        context.sharedOwner = true;
        context.totalVariables = totalVariables;
        context.functions = (SimFunction *) context.code->allocate( totalFunctions*sizeof(SimFunction) );
        context.totalFunctions = totalFunctions;
        auto debuggerOrGC = getDebugger()  || context.thisProgram->options.getBoolOption("gc",false);
        vector lookupFunctionTable;
        das_map sharedLookup;
        if ( totalFunctions ) {
            for (auto & pm : library.modules) {
                pm->functions.foreach([&](auto pfun){
                    if (pfun->index < 0 || !pfun->used)
                        return;
                    auto mangledName = pfun->getMangledName();
                    auto MNH = hash_blockz64((uint8_t *)mangledName.c_str());
                    if ( MNH==0 ) {
                        error("Internalc compiler errors. Mangled name hash is zero. Function " + pfun->name,
                            "\tMangled name " + mangledName + " hash is " + to_string(MNH), "",
                                pfun->at);
                    }
                    auto & gfun = context.functions[pfun->index];
                    gfun.name = context.code->allocateName(pfun->name);
                    gfun.mangledName = context.code->allocateName(mangledName);
                    gfun.debugInfo = helper.makeFunctionDebugInfo(*pfun);
                    if ( folding ) {
                        gfun.debugInfo->flags &= ~ (FuncInfo::flag_init | FuncInfo::flag_shutdown);
                    }
                    if ( debuggerOrGC ) {
                        helper.appendLocalVariables(gfun.debugInfo, pfun->body);
                    }
                    gfun.stackSize = pfun->totalStackSize;
                    gfun.mangledNameHash = MNH;
                    gfun.aotFunction = nullptr;
                    gfun.flags = 0;
                    gfun.fastcall = pfun->fastCall;
                    if ( pfun->module->builtIn && !pfun->module->promoted ) {
                        gfun.builtin = true;
                    }
                    gfun.code = pfun->simulate(context);
                    lookupFunctionTable.push_back(pfun);
                });
            }
        }
        if ( totalVariables ) {
            for (auto & pm : library.modules ) {
                pm->globals.foreach([&](auto pvar){
                    if (!pvar->used)
                        return;
                    auto & gvar = context.globalVariables[pvar->index];
                    if ( !folding && pvar->init ) {
                        if ( pvar->init->rtti_isMakeLocal() ) {
                            if ( pvar->global_shared ) {
                                auto sl = context.code->makeNode(pvar->init->at, pvar->stackTop, pvar->getMangledNameHash());
                                auto sr = ExprLet::simulateInit(context, pvar, false);
                                auto gvari = context.code->makeNode(pvar->init->at, sl, sr, uint32_t(sizeof(Prologue)));
                                auto cndb = context.code->makeNode(pvar->init->at, 1); // arg 1 of init script is "init_globals"
                                gvar.init = context.code->makeNode(pvar->init->at, cndb, gvari);
                            } else {
                                auto sl = context.code->makeNode(pvar->init->at, pvar->stackTop, pvar->getMangledNameHash());
                                auto sr = ExprLet::simulateInit(context, pvar, false);
                                gvar.init = context.code->makeNode(pvar->init->at, sl, sr, uint32_t(sizeof(Prologue)));
                            }
                        } else {
                            gvar.init = ExprLet::simulateInit(context, pvar, false);
                        }
                    } else {
                        gvar.init = nullptr;
                    }
                });
            }
        }
        //
        context.globalInitStackSize = globalInitStackSize;
        buildMNLookup(context, lookupFunctionTable, logs);
        buildGMNLookup(context, logs);
        buildADLookup(context, logs);
        context.simEnd();
        // if RTTI is enabled
        if (errors.size()) {
            isSimulating = false;
            return false;
        }
        bool aot_hint = policies.aot && !folding && !thisModule->isModule;
#if DAS_FUSION
        if ( !folding ) {               // note: only run fusion when not folding
            fusion(context, logs);
            context.relocateCode(true); // this to get better estimate on relocated size. its fust enough
        }
#else
        if ( !folding ) {
            context.relocateCode(true);
        }
#endif
        if ( !folding ) {
            if ( !aot_hint ) {
                context.relocateCode();
            }
        }
        context.restart();
        // now call annotation simulate
        das_hash_map indexToFunction;
        for (auto & pm : library.modules) {
            pm->functions.foreach([&](auto pfun){
                if (pfun->index < 0 || !pfun->used)
                    return;
                auto & gfun = context.functions[pfun->index];
                for ( const auto & an : pfun->annotations ) {
                    auto fna = static_pointer_cast(an->annotation);
                    if (!fna->simulate(&context, &gfun)) {
                        error("function " + pfun->describe() + " annotation " + fna->name + " simulation failed", "", "",
                            LineInfo(), CompilationError::cant_initialize);
                    }
                }
                indexToFunction[pfun->index] = pfun.get();
            });
        }
        // verify code and string heaps
#if DAS_FUSION
        if ( !folding ) {
            // note: this only matters if code has significant jumping around
            // which is always introduced by fusion
            DAS_ASSERTF(context.code->depth()depth()setIntern(options.getBoolOption("intern_strings", policies.intern_strings));
        // log all functions
        if ( options.getBoolOption("log_nodes",false) ) {
            bool displayHash = options.getBoolOption("log_nodes_aot_hash",false);
            for ( int i=0; i!=context.totalVariables; ++i ) {
                auto & pv = context.globalVariables[i];
                if ( pv.init ) {
                    logs index] = pfun.get();
            });
        }
        for ( int fni=0; fni!=context.totalFunctions; ++fni ) {
            if ( !fnn[fni]->noAot ) {
                SimFunction & fn = context.functions[fni];
                fnn[fni]->hash = getFunctionHash(fnn[fni], fn.code, &context);
            }
        }
        for ( int fni=0; fni!=context.totalFunctions; ++fni ) {
            if ( !fnn[fni]->noAot ) {
                SimFunction & fn = context.functions[fni];
                uint64_t semHash = fnn[fni]->aotHash = getFunctionAotHash(fnn[fni]);
                auto it = aotLib.find(semHash);
                if ( it != aotLib.end() ) {
                    fn.code = (it->second)(context);
                    fn.aot = true;
                    if ( logIt ) logs 

Web Proxy Viewer  |  New URL  |  Original Page