1065 lines
26 KiB
JavaScript
1065 lines
26 KiB
JavaScript
/*
|
|
* 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.
|
|
*/
|
|
"use strict";
|
|
|
|
class Arg {
|
|
constructor()
|
|
{
|
|
this._kind = Arg.Invalid;
|
|
}
|
|
|
|
static isAnyUse(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.Use:
|
|
case Arg.ColdUse:
|
|
case Arg.UseDef:
|
|
case Arg.UseZDef:
|
|
case Arg.LateUse:
|
|
case Arg.LateColdUse:
|
|
case Arg.Scratch:
|
|
return true;
|
|
case Arg.Def:
|
|
case Arg.ZDef:
|
|
case Arg.UseAddr:
|
|
case Arg.EarlyDef:
|
|
return false;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isColdUse(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.ColdUse:
|
|
case Arg.LateColdUse:
|
|
return true;
|
|
case Arg.Use:
|
|
case Arg.UseDef:
|
|
case Arg.UseZDef:
|
|
case Arg.LateUse:
|
|
case Arg.Def:
|
|
case Arg.ZDef:
|
|
case Arg.UseAddr:
|
|
case Arg.Scratch:
|
|
case Arg.EarlyDef:
|
|
return false;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isWarmUse(role)
|
|
{
|
|
return Arg.isAnyUse(role) && !Arg.isColdUse(role);
|
|
}
|
|
|
|
static cooled(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.ColdUse:
|
|
case Arg.LateColdUse:
|
|
case Arg.UseDef:
|
|
case Arg.UseZDef:
|
|
case Arg.Def:
|
|
case Arg.ZDef:
|
|
case Arg.UseAddr:
|
|
case Arg.Scratch:
|
|
case Arg.EarlyDef:
|
|
return role;
|
|
case Arg.Use:
|
|
return Arg.ColdUse;
|
|
case Arg.LateUse:
|
|
return Arg.LateColdUse;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isEarlyUse(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.Use:
|
|
case Arg.ColdUse:
|
|
case Arg.UseDef:
|
|
case Arg.UseZDef:
|
|
return true;
|
|
case Arg.Def:
|
|
case Arg.ZDef:
|
|
case Arg.UseAddr:
|
|
case Arg.LateUse:
|
|
case Arg.LateColdUse:
|
|
case Arg.Scratch:
|
|
case Arg.EarlyDef:
|
|
return false;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isLateUse(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.LateUse:
|
|
case Arg.LateColdUse:
|
|
case Arg.Scratch:
|
|
return true;
|
|
case Arg.ColdUse:
|
|
case Arg.Use:
|
|
case Arg.UseDef:
|
|
case Arg.UseZDef:
|
|
case Arg.Def:
|
|
case Arg.ZDef:
|
|
case Arg.UseAddr:
|
|
case Arg.EarlyDef:
|
|
return false;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isAnyDef(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.Use:
|
|
case Arg.ColdUse:
|
|
case Arg.UseAddr:
|
|
case Arg.LateUse:
|
|
case Arg.LateColdUse:
|
|
return false;
|
|
case Arg.Def:
|
|
case Arg.UseDef:
|
|
case Arg.ZDef:
|
|
case Arg.UseZDef:
|
|
case Arg.EarlyDef:
|
|
case Arg.Scratch:
|
|
return true;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isEarlyDef(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.Use:
|
|
case Arg.ColdUse:
|
|
case Arg.UseAddr:
|
|
case Arg.LateUse:
|
|
case Arg.Def:
|
|
case Arg.UseDef:
|
|
case Arg.ZDef:
|
|
case Arg.UseZDef:
|
|
case Arg.LateColdUse:
|
|
return false;
|
|
case Arg.EarlyDef:
|
|
case Arg.Scratch:
|
|
return true;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isLateDef(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.Use:
|
|
case Arg.ColdUse:
|
|
case Arg.UseAddr:
|
|
case Arg.LateUse:
|
|
case Arg.EarlyDef:
|
|
case Arg.Scratch:
|
|
case Arg.LateColdUse:
|
|
return false;
|
|
case Arg.Def:
|
|
case Arg.UseDef:
|
|
case Arg.ZDef:
|
|
case Arg.UseZDef:
|
|
return true;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static isZDef(role)
|
|
{
|
|
switch (role) {
|
|
case Arg.Use:
|
|
case Arg.ColdUse:
|
|
case Arg.UseAddr:
|
|
case Arg.LateUse:
|
|
case Arg.Def:
|
|
case Arg.UseDef:
|
|
case Arg.EarlyDef:
|
|
case Arg.Scratch:
|
|
case Arg.LateColdUse:
|
|
return false;
|
|
case Arg.ZDef:
|
|
case Arg.UseZDef:
|
|
return true;
|
|
default:
|
|
throw new Error("Bad role");
|
|
}
|
|
}
|
|
|
|
static typeForB3Type(type)
|
|
{
|
|
switch (type) {
|
|
case Int32:
|
|
case Int64:
|
|
return GP;
|
|
case Float:
|
|
case Double:
|
|
return FP;
|
|
default:
|
|
throw new Error("Bad B3 type");
|
|
}
|
|
}
|
|
|
|
static widthForB3Type(type)
|
|
{
|
|
switch (type) {
|
|
case Int32:
|
|
case Float:
|
|
return 32;
|
|
case Int64:
|
|
case Double:
|
|
return 64;
|
|
default:
|
|
throw new Error("Bad B3 type");
|
|
}
|
|
}
|
|
|
|
static conservativeWidth(type)
|
|
{
|
|
return type == GP ? Ptr : 64;
|
|
}
|
|
|
|
static minimumWidth(type)
|
|
{
|
|
return type == GP ? 8 : 32;
|
|
}
|
|
|
|
static bytes(width)
|
|
{
|
|
return width / 8;
|
|
}
|
|
|
|
static widthForBytes(bytes)
|
|
{
|
|
switch (bytes) {
|
|
case 0:
|
|
case 1:
|
|
return 8;
|
|
case 2:
|
|
return 16;
|
|
case 3:
|
|
case 4:
|
|
return 32;
|
|
default:
|
|
if (bytes > 8)
|
|
throw new Error("Bad number of bytes");
|
|
return 64;
|
|
}
|
|
}
|
|
|
|
static createTmp(tmp)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.Tmp;
|
|
result._tmp = tmp;
|
|
return result;
|
|
}
|
|
|
|
static fromReg(reg)
|
|
{
|
|
return Arg.createTmp(reg);
|
|
}
|
|
|
|
static createImm(value)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.Imm;
|
|
result._value = value;
|
|
return result;
|
|
}
|
|
|
|
static createBigImm(lowValue, highValue = 0)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.BigImm;
|
|
result._lowValue = lowValue;
|
|
result._highValue = highValue;
|
|
return result;
|
|
}
|
|
|
|
static createBitImm(value)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.BitImm;
|
|
result._value = value;
|
|
return result;
|
|
}
|
|
|
|
static createBitImm64(lowValue, highValue = 0)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.BitImm64;
|
|
result._lowValue = lowValue;
|
|
result._highValue = highValue;
|
|
return result;
|
|
}
|
|
|
|
static createAddr(base, offset = 0)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.Addr;
|
|
result._base = base;
|
|
result._offset = offset;
|
|
return result;
|
|
}
|
|
|
|
static createStack(slot, offset = 0)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.Stack;
|
|
result._slot = slot;
|
|
result._offset = offset;
|
|
return result;
|
|
}
|
|
|
|
static createCallArg(offset)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.CallArg;
|
|
result._offset = offset;
|
|
return result;
|
|
}
|
|
|
|
static createStackAddr(offsetFromFP, frameSize, width)
|
|
{
|
|
let result = Arg.createAddr(Reg.callFrameRegister, offsetFromFP);
|
|
if (!result.isValidForm(width))
|
|
result = Arg.createAddr(Reg.stackPointerRegister, offsetFromFP + frameSize);
|
|
return result;
|
|
}
|
|
|
|
static isValidScale(scale, width)
|
|
{
|
|
switch (scale) {
|
|
case 1:
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static logScale(scale)
|
|
{
|
|
switch (scale) {
|
|
case 1:
|
|
return 0;
|
|
case 2:
|
|
return 1;
|
|
case 4:
|
|
return 2;
|
|
case 8:
|
|
return 3;
|
|
default:
|
|
throw new Error("Bad scale");
|
|
}
|
|
}
|
|
|
|
static createIndex(base, index, scale = 1, offset = 0)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.Index;
|
|
result._base = base;
|
|
result._index = index;
|
|
result._scale = scale;
|
|
result._offset = offset;
|
|
return result;
|
|
}
|
|
|
|
static createRelCond(condition)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.RelCond;
|
|
result._condition = condition;
|
|
return result;
|
|
}
|
|
|
|
static createResCond(condition)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.ResCond;
|
|
result._condition = condition;
|
|
return result;
|
|
}
|
|
|
|
static createDoubleCond(condition)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.DoubleCond;
|
|
result._condition = condition;
|
|
return result;
|
|
}
|
|
|
|
static createWidth(width)
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.Width;
|
|
result._width = width;
|
|
return result;
|
|
}
|
|
|
|
static createSpecial()
|
|
{
|
|
let result = new Arg();
|
|
result._kind = Arg.Special;
|
|
return result;
|
|
}
|
|
|
|
get kind() { return this._kind; }
|
|
get isTmp() { return this._kind == Arg.Tmp; }
|
|
get isImm() { return this._kind == Arg.Imm; }
|
|
get isBigImm() { return this._kind == Arg.BigImm; }
|
|
get isBitImm() { return this._kind == Arg.BitImm; }
|
|
get isBitImm64() { return this._kind == Arg.BitImm64; }
|
|
get isSomeImm()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Imm:
|
|
case Arg.BitImm:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
get isSomeBigImm()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.BigImm:
|
|
case Arg.BitImm64:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
get isAddr() { return this._kind == Arg.Addr; }
|
|
get isStack() { return this._kind == Arg.Stack; }
|
|
get isCallArg() { return this._kind == Arg.CallArg; }
|
|
get isIndex() { return this._kind == Arg.Index; }
|
|
get isMemory()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Addr:
|
|
case Arg.Stack:
|
|
case Arg.CallArg:
|
|
case Arg.Index:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
get isStackMemory()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Addr:
|
|
return this._base == Reg.callFrameRegister
|
|
|| this._base == Reg.stackPointerRegister;
|
|
case Arg.Stack:
|
|
case Arg.CallArg:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
get isRelCond() { return this._kind == Arg.RelCond; }
|
|
get isResCond() { return this._kind == Arg.ResCond; }
|
|
get isDoubleCond() { return this._kind == Arg.DoubleCond; }
|
|
get isCondition()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.RelCond:
|
|
case Arg.ResCond:
|
|
case Arg.DoubleCond:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
get isWidth() { return this._kind == Arg.Width; }
|
|
get isSpecial() { return this._kind == Arg.Special; }
|
|
get isAlive() { return this.isTmp || this.isStack; }
|
|
|
|
get tmp()
|
|
{
|
|
if (this._kind != Arg.Tmp)
|
|
throw new Error("Called .tmp for non-tmp");
|
|
return this._tmp;
|
|
}
|
|
|
|
get value()
|
|
{
|
|
if (!this.isSomeImm)
|
|
throw new Error("Called .value for non-imm");
|
|
return this._value;
|
|
}
|
|
|
|
get lowValue()
|
|
{
|
|
if (!this.isSomeBigImm)
|
|
throw new Error("Called .lowValue for non-big-imm");
|
|
return this._lowValue;
|
|
}
|
|
|
|
get highValue()
|
|
{
|
|
if (!this.isSomeBigImm)
|
|
throw new Error("Called .highValue for non-big-imm");
|
|
return this._highValue;
|
|
}
|
|
|
|
get base()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Addr:
|
|
case Arg.Index:
|
|
return this._base;
|
|
default:
|
|
throw new Error("Called .base for non-address");
|
|
}
|
|
}
|
|
|
|
get hasOffset() { return this.isMemory; }
|
|
|
|
get offset()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Addr:
|
|
case Arg.Index:
|
|
case Arg.Stack:
|
|
case Arg.CallArg:
|
|
return this._offset;
|
|
default:
|
|
throw new Error("Called .offset for non-address");
|
|
}
|
|
}
|
|
|
|
get stackSlot()
|
|
{
|
|
if (this._kind != Arg.Stack)
|
|
throw new Error("Called .stackSlot for non-address");
|
|
return this._slot;
|
|
}
|
|
|
|
get index()
|
|
{
|
|
if (this._kind != Arg.Index)
|
|
throw new Error("Called .index for non-Index");
|
|
return this._index;
|
|
}
|
|
|
|
get scale()
|
|
{
|
|
if (this._kind != Arg.Index)
|
|
throw new Error("Called .scale for non-Index");
|
|
return this._scale;
|
|
}
|
|
|
|
get logScale()
|
|
{
|
|
return Arg.logScale(this.scale);
|
|
}
|
|
|
|
get width()
|
|
{
|
|
if (this._kind != Arg.Width)
|
|
throw new Error("Called .width for non-Width");
|
|
return this._width;
|
|
}
|
|
|
|
get isGPTmp() { return this.isTmp && this.tmp.isGP; }
|
|
get isFPTmp() { return this.isTmp && this.tmp.isFP; }
|
|
|
|
get isGP()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Imm:
|
|
case Arg.BigImm:
|
|
case Arg.BitImm:
|
|
case Arg.BitImm64:
|
|
case Arg.Addr:
|
|
case Arg.Index:
|
|
case Arg.Stack:
|
|
case Arg.CallArg:
|
|
case Arg.RelCond:
|
|
case Arg.ResCond:
|
|
case Arg.DoubleCond:
|
|
case Arg.Width:
|
|
case Arg.Special:
|
|
return true;
|
|
case Arg.Tmp:
|
|
return this.isGPTmp;
|
|
case Arg.Invalid:
|
|
return false;
|
|
default:
|
|
throw new Error("Bad kind");
|
|
}
|
|
}
|
|
|
|
get isFP()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Imm:
|
|
case Arg.BitImm:
|
|
case Arg.BitImm64:
|
|
case Arg.RelCond:
|
|
case Arg.ResCond:
|
|
case Arg.DoubleCond:
|
|
case Arg.Width:
|
|
case Arg.Special:
|
|
case Arg.Invalid:
|
|
return false;
|
|
case Arg.Addr:
|
|
case Arg.Index:
|
|
case Arg.Stack:
|
|
case Arg.CallArg:
|
|
case Arg.BigImm:
|
|
return true;
|
|
case Arg.Tmp:
|
|
return this.isFPTmp;
|
|
default:
|
|
throw new Error("Bad kind");
|
|
}
|
|
}
|
|
|
|
get hasType()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Imm:
|
|
case Arg.BitImm:
|
|
case Arg.BitImm64:
|
|
case Arg.Tmp:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
get type()
|
|
{
|
|
return this.isGP ? GP : FP;
|
|
}
|
|
|
|
isType(type)
|
|
{
|
|
switch (type) {
|
|
case Arg.GP:
|
|
return this.isGP;
|
|
case Arg.FP:
|
|
return this.isFP;
|
|
default:
|
|
throw new Error("Bad type");
|
|
}
|
|
}
|
|
|
|
isCompatibleType(other)
|
|
{
|
|
if (this.hasType)
|
|
return other.isType(this.type);
|
|
if (other.hasType)
|
|
return this.isType(other.type);
|
|
return true;
|
|
}
|
|
|
|
get isGPR() { return this.isTmp && this.tmp.isGPR; }
|
|
get gpr() { return this.tmp.gpr; }
|
|
get isFPR() { return this.isTmp && this.tmp.isFPR; }
|
|
get fpr() { return this.tmp.fpr; }
|
|
get isReg() { return this.isTmp && this.tmp.isReg; }
|
|
get reg() { return this.tmp.reg; }
|
|
|
|
static isValidImmForm(value)
|
|
{
|
|
return isRepresentableAsInt32(value);
|
|
}
|
|
static isValidBitImmForm(value)
|
|
{
|
|
return isRepresentableAsInt32(value);
|
|
}
|
|
static isValidBitImm64Form(value)
|
|
{
|
|
return isRepresentableAsInt32(value);
|
|
}
|
|
|
|
static isValidAddrForm(offset, width)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static isValidIndexForm(scale, offset, width)
|
|
{
|
|
if (!isValidScale(scale, width))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
isValidForm(width)
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Invalid:
|
|
return false;
|
|
case Arg.Tmp:
|
|
return true;
|
|
case Arg.Imm:
|
|
return Arg.isValidImmForm(this.value);
|
|
case Arg.BigImm:
|
|
return true;
|
|
case Arg.BitImm:
|
|
return Arg.isValidBitImmForm(this.value);
|
|
case Arg.BitImm64:
|
|
return Arg.isValidBitImm64Form(this.value);
|
|
case Arg.Addr:
|
|
case Arg.Stack:
|
|
case Arg.CallArg:
|
|
return Arg.isValidAddrForm(this.offset, width);
|
|
case Arg.Index:
|
|
return Arg.isValidIndexForm(this.scale, this.offset, width);
|
|
case Arg.RelCond:
|
|
case Arg.ResCond:
|
|
case Arg.DoubleCond:
|
|
case Arg.Width:
|
|
case Arg.Special:
|
|
return true;
|
|
default:
|
|
throw new Error("Bad kind");
|
|
}
|
|
}
|
|
|
|
forEachTmpFast(func)
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Tmp: {
|
|
let replacement;
|
|
if (replacement = func(this._tmp))
|
|
return Arg.createTmp(replacement);
|
|
break;
|
|
}
|
|
case Arg.Addr: {
|
|
let replacement;
|
|
if (replacement = func(this._base))
|
|
return Arg.createAddr(replacement, this._offset);
|
|
break;
|
|
}
|
|
case Arg.Index: {
|
|
let baseReplacement = func(this._base);
|
|
let indexReplacement = func(this._index);
|
|
if (baseReplacement || indexReplacement) {
|
|
return Arg.createIndex(
|
|
baseReplacement ? baseReplacement : this._base,
|
|
indexReplacement ? indexReplacement : this._index,
|
|
this._scale, this._offset);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
usesTmp(expectedTmp)
|
|
{
|
|
let usesTmp = false;
|
|
forEachTmpFast(tmp => {
|
|
usesTmp |= tmp == expectedTmp;
|
|
});
|
|
return usesTmp;
|
|
}
|
|
|
|
forEachTmp(role, type, width, func)
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Tmp: {
|
|
let replacement;
|
|
if (replacement = func(this._tmp, role, type, width))
|
|
return Arg.createTmp(replacement);
|
|
break;
|
|
}
|
|
case Arg.Addr: {
|
|
let replacement;
|
|
if (replacement = func(this._base, Arg.Use, GP, role == Arg.UseAddr ? width : Ptr))
|
|
return Arg.createAddr(replacement, this._offset);
|
|
break;
|
|
}
|
|
case Arg.Index: {
|
|
let baseReplacement = func(this._base, Arg.Use, GP, role == Arg.UseAddr ? width : Ptr);
|
|
let indexReplacement = func(this._index, Arg.Use, GP, role == Arg.UseAddr ? width : Ptr);
|
|
if (baseReplacement || indexReplacement) {
|
|
return Arg.createIndex(
|
|
baseReplacement ? baseReplacement : this._base,
|
|
indexReplacement ? indexReplacement : this._index,
|
|
this._scale, this._offset);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
is(thing) { return !!thing.extract(this); }
|
|
as(thing) { return thing.extract(this); }
|
|
|
|
// This lets you say things like:
|
|
// arg.forEach(Tmp | Reg | Arg | StackSlot, ...)
|
|
//
|
|
// It's used for abstract liveness analysis.
|
|
forEachFast(thing, func)
|
|
{
|
|
return thing.forEachFast(this, func);
|
|
}
|
|
forEach(thing, role, type, width, func)
|
|
{
|
|
return thing.forEach(this, role, type, width, func);
|
|
}
|
|
|
|
static extract(arg) { return arg; }
|
|
static forEachFast(arg, func) { return func(arg); }
|
|
static forEach(arg, role, type, width, func) { return func(arg, role, type, width); }
|
|
|
|
get condition()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.RelCond:
|
|
case Arg.ResCond:
|
|
case Arg.DoubleCond:
|
|
return this._condition;
|
|
default:
|
|
throw new Error("Called .condition for non-condition");
|
|
}
|
|
}
|
|
|
|
get isInvertible()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.RelCond:
|
|
case Arg.DoubleCold:
|
|
return true;
|
|
case Arg.ResCond:
|
|
switch (this._condition) {
|
|
case Zero:
|
|
case NonZero:
|
|
case Signed:
|
|
case PositiveOrZero:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static kindCode(kind)
|
|
{
|
|
switch (kind) {
|
|
case Arg.Invalid:
|
|
return 0;
|
|
case Arg.Tmp:
|
|
return 1;
|
|
case Arg.Imm:
|
|
return 2;
|
|
case Arg.BigImm:
|
|
return 3;
|
|
case Arg.BitImm:
|
|
return 4;
|
|
case Arg.BitImm64:
|
|
return 5;
|
|
case Arg.Addr:
|
|
return 6;
|
|
case Arg.Stack:
|
|
return 7;
|
|
case Arg.CallArg:
|
|
return 8;
|
|
case Arg.Index:
|
|
return 9;
|
|
case Arg.RelCond:
|
|
return 10;
|
|
case Arg.ResCond:
|
|
return 11;
|
|
case Arg.DoubleCond:
|
|
return 12;
|
|
case Arg.Special:
|
|
return 13;
|
|
case Arg.WidthArg:
|
|
return 14;
|
|
default:
|
|
throw new Error("Bad kind");
|
|
}
|
|
}
|
|
|
|
hash()
|
|
{
|
|
let result = Arg.kindCode(this._kind);
|
|
|
|
switch (this._kind) {
|
|
case Arg.Invalid:
|
|
case Arg.Special:
|
|
break;
|
|
case Arg.Tmp:
|
|
result += this._tmp.hash();
|
|
result |= 0;
|
|
break;
|
|
case Arg.Imm:
|
|
case Arg.BitImm:
|
|
result += this._value;
|
|
result |= 0;
|
|
break;
|
|
case Arg.BigImm:
|
|
case Arg.BitImm64:
|
|
result += this._lowValue;
|
|
result |= 0;
|
|
result += this._highValue;
|
|
result |= 0;
|
|
break;
|
|
case Arg.CallArg:
|
|
result += this._offset;
|
|
result |= 0;
|
|
break;
|
|
case Arg.RelCond:
|
|
result += relCondCode(this._condition);
|
|
result |= 0;
|
|
break;
|
|
case Arg.ResCond:
|
|
result += resCondCode(this._condition);
|
|
result |= 0;
|
|
break;
|
|
case Arg.DoubleCond:
|
|
result += doubleCondCode(this._condition);
|
|
result |= 0;
|
|
break;
|
|
case Arg.WidthArg:
|
|
result += this._width;
|
|
result |= 0;
|
|
break;
|
|
case Arg.Addr:
|
|
result += this._offset;
|
|
result |= 0;
|
|
result += this._base.hash();
|
|
result |= 0;
|
|
break;
|
|
case Arg.Index:
|
|
result += this._offset;
|
|
result |= 0;
|
|
result += this._scale;
|
|
result |= 0;
|
|
result += this._base.hash();
|
|
result |= 0;
|
|
result += this._index.hash();
|
|
result |= 0;
|
|
break;
|
|
case Arg.Stack:
|
|
result += this._offset;
|
|
result |= 0;
|
|
result += this.stackSlot.index;
|
|
result |= 0;
|
|
break;
|
|
}
|
|
|
|
return result >>> 0;
|
|
}
|
|
|
|
toString()
|
|
{
|
|
switch (this._kind) {
|
|
case Arg.Invalid:
|
|
return "<invalid>";
|
|
case Arg.Tmp:
|
|
return this._tmp.toString();
|
|
case Arg.Imm:
|
|
return "$" + this._value;
|
|
case Arg.BigImm:
|
|
case Arg.BitImm64:
|
|
return "$0x" + this._highValue.toString(16) + ":" + this._lowValue.toString(16);
|
|
case Arg.Addr:
|
|
return "" + (this._offset ? this._offset : "") + "(" + this._base + ")";
|
|
case Arg.Index:
|
|
return "" + (this._offset ? this._offset : "") + "(" + this._base +
|
|
"," + this._index + (this._scale == 1 ? "" : "," + this._scale) + ")";
|
|
case Arg.Stack:
|
|
return "" + (this._offset ? this._offset : "") + "(" + this._slot + ")";
|
|
case Arg.CallArg:
|
|
return "" + (this._offset ? this._offset : "") + "(callArg)";
|
|
case Arg.RelCond:
|
|
case Arg.ResCond:
|
|
case Arg.DoubleCond:
|
|
return symbolName(this._condition);
|
|
case Arg.Special:
|
|
return "special";
|
|
case Arg.Width:
|
|
return "" + this._value;
|
|
default:
|
|
throw new Error("Bad kind");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Arg kinds
|
|
Arg.Invalid = Symbol("Invalid");
|
|
Arg.Tmp = Symbol("Tmp");
|
|
Arg.Imm = Symbol("Imm");
|
|
Arg.BigImm = Symbol("BigImm");
|
|
Arg.BitImm = Symbol("BitImm");
|
|
Arg.BitImm64 = Symbol("BitImm64");
|
|
Arg.Addr = Symbol("Addr");
|
|
Arg.Stack = Symbol("Stack");
|
|
Arg.CallArg = Symbol("CallArg");
|
|
Arg.Index = Symbol("Index");
|
|
Arg.RelCond = Symbol("RelCond");
|
|
Arg.ResCond = Symbol("ResCond");
|
|
Arg.DoubleCond = Symbol("DoubleCond");
|
|
Arg.Special = Symbol("Special");
|
|
Arg.Width = Symbol("Width");
|
|
|
|
// Arg roles
|
|
Arg.Use = Symbol("Use");
|
|
Arg.ColdUse = Symbol("ColdUse");
|
|
Arg.LateUse = Symbol("LateUse");
|
|
Arg.LateColdUse = Symbol("LateColdUse");
|
|
Arg.Def = Symbol("Def");
|
|
Arg.ZDef = Symbol("ZDef");
|
|
Arg.UseDef = Symbol("UseDef");
|
|
Arg.UseZDef = Symbol("UseZDef");
|
|
Arg.EarlyDef = Symbol("EarlyDef");
|
|
Arg.Scratch = Symbol("Scratch");
|
|
Arg.UseAddr = Symbol("UseAddr");
|
|
|