blob: 07aceda8d5eb2134ad3d8e046922c27eed8a5998 [file] [log] [blame]
/*
* 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");