AirDumpAsJS.cpp   [plain text]


/*
 * Copyright (C) 2016 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "AirDumpAsJS.h"

#if ENABLE(B3_JIT)

#include "AirCode.h"
#include "AirInstInlines.h"

namespace JSC { namespace B3 { namespace Air {

namespace {

CString varNameForBlockAtIndex(unsigned index)
{
    return toCString("bb", index);
}

CString varName(BasicBlock* block)
{
    return varNameForBlockAtIndex(block->index());
}

CString varNameForStackSlotAtIndex(unsigned index)
{
    return toCString("slot", index);
}

CString varName(StackSlot* slot)
{
    return varNameForStackSlotAtIndex(slot->index());
}

CString varName(Reg reg)
{
    return toCString("Reg.", reg.debugName());
}

CString varNameForTmpWithTypeAndIndex(Arg::Type type, unsigned index)
{
    return toCString(type == Arg::FP ? "f" : "", "tmp", index);
}

CString varName(Tmp tmp)
{
    if (tmp.isReg())
        return varName(tmp.reg());
    return varNameForTmpWithTypeAndIndex(Arg(tmp).type(), tmp.tmpIndex());
}

} // anonymous namespace

void dumpAsJS(Code& code, PrintStream& out)
{
    out.println("let code = new Code();");
    
    for (unsigned i = 0; i < code.size(); ++i)
        out.println("let ", varNameForBlockAtIndex(i), " = code.addBlock();");
    
    out.println("let hash;");

    for (unsigned i = 0; i < code.stackSlots().size(); ++i) {
        StackSlot* slot = code.stackSlots()[i];
        if (slot) {
            out.println("let ", varName(slot), " = code.addStackSlot(", slot->byteSize(), ", ", slot->kind(), ");");
            if (slot->offsetFromFP())
                out.println(varName(slot), ".setOffsetFromFP(", slot->offsetFromFP(), ");");
            out.println("hash = ", varName(slot), ".hash();");
            out.println("if (hash != ", slot->jsHash(), ")");
            out.println("    throw new Error(\"Bad hash: \" + hash);");
        } else
            out.println("code.addStackSlot(1, Spill);");
    }
    
    Arg::forEachType(
        [&] (Arg::Type type) {
            for (unsigned i = code.numTmps(type); i--;) {
                out.println(
                    "let ", varNameForTmpWithTypeAndIndex(type, i), " = code.newTmp(", type, ");");
            }
        });
    
    out.println("let inst;");
    out.println("let arg;");
    
    for (BasicBlock* block : code) {
        for (FrequentedBlock successor : block->successors()) {
            out.println(
                varName(block), ".successors.push(new FrequentedBlock(",
                varName(successor.block()), ", ", successor.frequency(), "));");
        }
        
        for (BasicBlock* predecessor : block->predecessors())
            out.println(varName(block), ".predecessors.push(", varName(predecessor), ");");
        
        for (Inst& inst : *block) {
            out.println("inst = new Inst(", inst.opcode, ");");
            
            inst.forEachArg(
                [&] (Arg& arg, Arg::Role, Arg::Type, Arg::Width) {
                    switch (arg.kind()) {
                    case Arg::Invalid:
                        RELEASE_ASSERT_NOT_REACHED();
                        break;
                        
                    case Arg::Tmp:
                        out.println("arg = Arg.createTmp(", varName(arg.tmp()), ");");
                        break;
                        
                    case Arg::Imm:
                        out.println("arg = Arg.createImm(", arg.value(), ");");
                        break;
                        
                    case Arg::BigImm:
                        out.println(
                            "arg = Arg.createBigImm(",
                            static_cast<int32_t>(arg.value()), ", ",
                            static_cast<int32_t>(arg.value() >> 32), ");");
                        break;
                        
                    case Arg::BitImm:
                        out.println("arg = Arg.createBitImm(", arg.value(), ");");
                        break;
                        
                    case Arg::BitImm64:
                        out.println(
                            "arg = Arg.createBitImm64(",
                            static_cast<int32_t>(arg.value()), ", ",
                            static_cast<int32_t>(arg.value() >> 32), ");");
                        break;
                        
                    case Arg::Addr:
                        out.println(
                            "arg = Arg.createAddr(", varName(arg.base()), ", ", arg.offset(), ");");
                        break;
                        
                    case Arg::Stack:
                        out.println(
                            "arg = Arg.createStack(", varName(arg.stackSlot()), ", ", arg.offset(), ");");
                        break;
                        
                    case Arg::CallArg:
                        out.println("arg = Arg.createCallArg(", arg.offset(), ");");
                        break;
                        
                    case Arg::Index:
                        out.println(
                            "arg = Arg.createIndex(", varName(arg.base()), ", ",
                            varName(arg.index()), ", ", arg.scale(), ", ", arg.offset(), ");");
                        break;
                        
                    case Arg::RelCond:
                        out.println("arg = Arg.createRelCond(", arg.asRelationalCondition(), ");");
                        break;
                        
                    case Arg::ResCond:
                        out.println("arg = Arg.createResCond(", arg.asResultCondition(), ");");
                        break;
                        
                    case Arg::DoubleCond:
                        out.println("arg = Arg.createDoubleCond(", arg.asDoubleCondition(), ");");
                        break;
                        
                    case Arg::Special:
                        out.println("arg = Arg.createSpecial();");
                        break;
                        
                    case Arg::WidthArg:
                        out.println("arg = Arg.createWidthArg(", arg.width(), ");");
                        break;
                    }
                    
                    out.println("inst.args.push(arg);");
                });
            
            if (inst.opcode == Patch) {
                if (inst.hasNonArgEffects())
                    out.println("inst.patchHasNonArgEffects = true;");
                
                out.println("inst.extraEarlyClobberedRegs = new Set();");
                out.println("inst.extraClobberedRegs = new Set();");
                inst.extraEarlyClobberedRegs().forEach(
                    [&] (Reg reg) {
                        out.println("inst.extraEarlyClobberedRegs.add(", varName(reg), ");");
                    });
                inst.extraClobberedRegs().forEach(
                    [&] (Reg reg) {
                        out.println("inst.extraClobberedRegs.add(", varName(reg), ");");
                    });
                
                out.println("inst.patchArgData = [];");
                inst.forEachArg(
                    [&] (Arg&, Arg::Role role, Arg::Type type, Arg::Width width) {
                        out.println(
                            "inst.patchArgData.push({role: Arg.", role, ", type: ", type,
                            ", width: ", width, "});");
                    });
            }
            
            if (inst.opcode == CCall || inst.opcode == ColdCCall) {
                out.println("inst.cCallType = ", inst.origin->type());
                out.println("inst.cCallArgTypes = [];");
                for (unsigned i = 1; i < inst.origin->numChildren(); ++i)
                    out.println("inst.cCallArgTypes.push(", inst.origin->child(i)->type(), ");");
            }
            
            out.println("hash = inst.hash();");
            out.println("if (hash != ", inst.jsHash(), ")");
            out.println("    throw new Error(\"Bad hash: \" + hash);");
            
            out.println(varName(block), ".append(inst);");
        }
    }
}

} } } // namespace JSC::B3::Air

#endif // ENABLE(B3_JIT)