Skip to content

Commit

Permalink
Refactored branch instructions with renaming ILoop into IWhile (#232)
Browse files Browse the repository at this point in the history
Co-authored-by: tmdghks <tmdghks12@korea.ac.kr>
  • Loading branch information
2 people authored and jhnaldo committed Oct 29, 2024
1 parent 47a9ecb commit 97b2de4
Show file tree
Hide file tree
Showing 20 changed files with 72 additions and 61 deletions.
2 changes: 1 addition & 1 deletion src/main/resources/manuals/funcs/IsInTailPosition.ir
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def IsInTailPosition(
// Let _body_ be the |FunctionBody|, |ConciseBody|, or |AsyncConciseBody| that most closely contains _call_.
%0 = false
%1 = call
loop[repeat] (&& (! %0) (! (= %1 absent))) {
while (&& (! %0) (! (= %1 absent))) {
%0 = (|| (|| (|| %0 (? %1: "FunctionBody")) (? %1: "ConciseBody")) (? %1: "AsyncConciseBody"))
if (! %0) %1 = %1.parent else {}
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/resources/manuals/rule.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
"Create an own accessor property named _P_ of object _O_ whose [[Get]], [[Set]], [[Enumerable]], and [[Configurable]] attributes are set to the value of the corresponding field in _Desc_ if _Desc_ has that field, or to the attribute's <emu-xref href=\"#table-object-property-attributes\">default value</emu-xref> otherwise.": "{ let ap = (new PropertyDescriptor()) if (= Desc.Get absent) ap.Get = undefined else ap.Get = Desc.Get if (= Desc.Set absent) ap.Set = undefined else ap.Set = Desc.Set if (= Desc.Enumerable absent) ap.Enumerable = false else ap.Enumerable = Desc.Enumerable if (= Desc.Configurable absent) ap.Configurable = false else ap.Configurable = Desc.Configurable O.SubMap[P] = ap }",
"Create an own data property named _P_ of object _O_ whose [[Value]], [[Writable]], [[Enumerable]], and [[Configurable]] attributes are set to the value of the corresponding field in _Desc_ if _Desc_ has that field, or to the attribute's <emu-xref href=\"#table-object-property-attributes\">default value</emu-xref> otherwise.": "{ let dp = (new PropertyDescriptor()) if (= Desc.Value absent) dp.Value = undefined else dp.Value = Desc.Value if (= Desc.Writable absent) dp.Writable = false else dp.Writable = Desc.Writable if (= Desc.Enumerable absent) dp.Enumerable = false else dp.Enumerable = Desc.Enumerable if (= Desc.Configurable absent) dp.Configurable = false else dp.Configurable = Desc.Configurable O.SubMap[P] = dp }",
"Create any host-defined global object properties on _globalObj_.": "nop",
"For each field of _Desc_, set the corresponding attribute of the property named _P_ of object _O_ to the value of the field.": "{ let fields = (keys Desc) let idx = 0 loop[repeat] (< idx fields.length) { let f = fields[idx] O.SubMap[P][f] = Desc[f] idx = (+ idx 1) } }",
"For each property of the Global Object specified in clause <emu-xref href=\"#sec-global-object\"></emu-xref>, do\n 1. Let _name_ be the String value of the property name.\n 1. Let _desc_ be the fully populated data Property Descriptor for the property, containing the specified attributes for the property. For properties listed in <emu-xref href=\"#sec-function-properties-of-the-global-object\"></emu-xref>, <emu-xref href=\"#sec-constructor-properties-of-the-global-object\"></emu-xref>, or <emu-xref href=\"#sec-other-properties-of-the-global-object\"></emu-xref> the value of the [[Value]] attribute is the corresponding intrinsic object from _realmRec_.\n 1. Perform ? DefinePropertyOrThrow(_global_, _name_, _desc_).": "{ let keys = (keys @GLOBAL.SubMap) let idx = 0 loop[repeat] (< idx keys.length) { let name = keys[idx] global.SubMap[name] = @GLOBAL.SubMap[name] idx = (+ idx 1) } global.SubMap.globalThis = (new PropertyDescriptor(\"Value\" -> global, \"Writable\" -> true, \"Enumerable\" -> false, \"Configurable\" -> true)) }",
"For each field of _Desc_, set the corresponding attribute of the property named _P_ of object _O_ to the value of the field.": "{ let fields = (keys Desc) let idx = 0 while (< idx fields.length) { let f = fields[idx] O.SubMap[P][f] = Desc[f] idx = (+ idx 1) } }",
"For each property of the Global Object specified in clause <emu-xref href=\"#sec-global-object\"></emu-xref>, do\n 1. Let _name_ be the String value of the property name.\n 1. Let _desc_ be the fully populated data Property Descriptor for the property, containing the specified attributes for the property. For properties listed in <emu-xref href=\"#sec-function-properties-of-the-global-object\"></emu-xref>, <emu-xref href=\"#sec-constructor-properties-of-the-global-object\"></emu-xref>, or <emu-xref href=\"#sec-other-properties-of-the-global-object\"></emu-xref> the value of the [[Value]] attribute is the corresponding intrinsic object from _realmRec_.\n 1. Perform ? DefinePropertyOrThrow(_global_, _name_, _desc_).": "{ let keys = (keys @GLOBAL.SubMap) let idx = 0 while (< idx keys.length) { let name = keys[idx] global.SubMap[name] = @GLOBAL.SubMap[name] idx = (+ idx 1) } global.SubMap.globalThis = (new PropertyDescriptor(\"Value\" -> global, \"Writable\" -> true, \"Enumerable\" -> false, \"Configurable\" -> true)) }",
"If the host is a web browser, then\n 1. Perform ? HostEnsureCanAddPrivateElement(_O_).": "nop",
"If _Desc_ does not have any fields, return *true*.": "{ let descKeys = (keys Desc) if (= descKeys.length 0) return true else {} }",
"If _O_ does not have an own property with key _P_, return *undefined*.": "if (= O.SubMap[P] absent) return undefined else {}",
Expand All @@ -59,17 +59,17 @@
"Let _args_ be the List of arguments that was passed to this function by [[Call]] or [[Construct]].": "let args = ArgumentsList",
"Let _asyncContext_ be a copy of _runningContext_.": "let asyncContext = (copy runningContext)",
"Let _contained_ be the result of _child_ Contains _symbol_.": "{ sdo-call result = child->Contains(symbol) let contained = result }",
"Let _ec_ be the topmost execution context on the execution context stack whose ScriptOrModule component is not *null*.": "{ let ec = absent let idx = 0 loop[repeat] (&& (< idx @EXECUTION_STACK.length) (= ec absent)) { if (! (= @EXECUTION_STACK[idx].ScriptOrModule null) ) ec = @EXECUTION_STACK[idx] else {} idx = (+ idx 1) } }",
"Let _ec_ be the topmost execution context on the execution context stack whose ScriptOrModule component is not *null*.": "{ let ec = absent let idx = 0 while (&& (< idx @EXECUTION_STACK.length) (= ec absent)) { if (! (= @EXECUTION_STACK[idx].ScriptOrModule null) ) ec = @EXECUTION_STACK[idx] else {} idx = (+ idx 1) } }",
"Let _func_ be a new built-in function object that, when called, performs the action described by _behaviour_ using the provided arguments as the values of the corresponding parameters specified by _behaviour_. The new function object has internal slots whose names are the elements of _internalSlotsList_, and an [[InitialName]] internal slot.": "{ let func = (new BuiltinFunctionObject()) func.Code = behaviour }",
"Let _instantiatedVarNames_ be a copy of the List _parameterBindings_.": "let instantiatedVarNames = (copy parameterBindings)",
"Let _internalSlotsList_ be a List containing the names of all the internal slots that <emu-xref href=\"#sec-built-in-function-objects\"></emu-xref> requires for the built-in function object that is about to be created.": "let internalSlotsList = (new [\"Prototype\", \"Extensible\", \"Realm\", \"InitialName\"])",
"Let _newLenDesc_ be a copy of _Desc_.": "let newLenDesc = (copy Desc)",
"Let _nextPending_ be the PendingJob record at the front of _nextQueue_. Remove that record from _nextQueue_.": "let nextPending = (pop < @JOB_QUEUE)",
"Let _nextQueue_ be a non-empty Job Queue chosen in an implementation-defined manner.": "let nextQueue = @JOB_QUEUE",
"Let _obj_ be a newly created object with an internal slot for each name in _internalSlotsList_.": "{ let obj = (new Object()) let idx = 0 loop[repeat] (< idx internalSlotsList.length) { obj[internalSlotsList[idx]] = undefined idx = (+ idx 1) } }",
"Let _obj_ be a newly created object with an internal slot for each name in _internalSlotsList_.": "{ let obj = (new Object()) let idx = 0 while (< idx internalSlotsList.length) { obj[internalSlotsList[idx]] = undefined idx = (+ idx 1) } }",
"Let _opText_ be the sequence of Unicode code points associated with _assignmentOpText_ in the following table:\n <figure>\n <!-- emu-format ignore -->\n <table class=\"lightweight-table\">\n <tbody><tr><th> _assignmentOpText_ </th><th> _opText_ </th></tr>\n <tr><td> `**=` </td><td> `**` </td></tr>\n <tr><td> `*=` </td><td> `*` </td></tr>\n <tr><td> `/=` </td><td> `/` </td></tr>\n <tr><td> `%=` </td><td> `%` </td></tr>\n <tr><td> `+=` </td><td> `+` </td></tr>\n <tr><td> `-=` </td><td> `-` </td></tr>\n <tr><td> `<<=` </td><td> `<<` </td></tr>\n <tr><td> `>>=` </td><td> `>>` </td></tr>\n <tr><td> `>>>=` </td><td> `>>>` </td></tr>\n <tr><td> `&=` </td><td> `&` </td></tr>\n <tr><td> `^=` </td><td> `^` </td></tr>\n <tr><td> `|=` </td><td> `|` </td></tr>\n </tbody></table>\n </figure>": "{ if (= assignmentOpText \"**=\") let opText = \"**\" else {} if (= assignmentOpText \"*=\") let opText = \"*\" else {} if (= assignmentOpText \"/=\") let opText = \"/\" else {} if (= assignmentOpText \"%=\") let opText = \"%\" else {} if (= assignmentOpText \"+=\") let opText = \"+\" else {} if (= assignmentOpText \"-=\") let opText = \"-\" else {} if (= assignmentOpText \"<<=\") let opText = \"<<\" else {} if (= assignmentOpText \">>=\") let opText = \">>\" else {} if (= assignmentOpText \">>>=\") let opText = \">>>\" else {} if (= assignmentOpText \"&=\") let opText = \"&\" else {} if (= assignmentOpText \"^=\") let opText = \"^\" else {} if (= assignmentOpText \"|=\") let opText = \"|\" else {} }",
"Let _operation_ be the abstract operation associated with _opText_ and Type(_lnum_) in the following table:\n <figure>\n <!-- emu-format ignore -->\n <table class=\"lightweight-table\">\n <tbody><tr><th> _opText_ </th><th> Type(_lnum_) </th><th> _operation_ </th></tr>\n <tr><td> `**` </td><td> Number </td><td> Number::exponentiate </td></tr>\n <tr><td> `*` </td><td> Number </td><td> Number::multiply </td></tr>\n <tr><td> `*` </td><td> BigInt </td><td> BigInt::multiply </td></tr>\n <tr><td> `/` </td><td> Number </td><td> Number::divide </td></tr>\n <tr><td> `%` </td><td> Number </td><td> Number::remainder </td></tr>\n <tr><td> `+` </td><td> Number </td><td> Number::add </td></tr>\n <tr><td> `+` </td><td> BigInt </td><td> BigInt::add </td></tr>\n <tr><td> `-` </td><td> Number </td><td> Number::subtract </td></tr>\n <tr><td> `-` </td><td> BigInt </td><td> BigInt::subtract </td></tr>\n <tr><td> `<<` </td><td> Number </td><td> Number::leftShift </td></tr>\n <tr><td> `<<` </td><td> BigInt </td><td> BigInt::leftShift </td></tr>\n <tr><td> `>>` </td><td> Number </td><td> Number::signedRightShift </td></tr>\n <tr><td> `>>` </td><td> BigInt </td><td> BigInt::signedRightShift </td></tr>\n <tr><td> `>>>` </td><td> Number </td><td> Number::unsignedRightShift </td></tr>\n <tr><td> `&` </td><td> Number </td><td> Number::bitwiseAND </td></tr>\n <tr><td> `&` </td><td> BigInt </td><td> BigInt::bitwiseAND </td></tr>\n <tr><td> `^` </td><td> Number </td><td> Number::bitwiseXOR </td></tr>\n <tr><td> `^` </td><td> BigInt </td><td> BigInt::bitwiseXOR </td></tr>\n <tr><td> `|` </td><td> Number </td><td> Number::bitwiseOR </td></tr>\n <tr><td> `|` </td><td> BigInt </td><td> BigInt::bitwiseOR </td></tr>\n </tbody></table>\n </figure>": "if (? lnum: \"Number\") { if (= opText \"**\") let operation = clo<Number::exponentiate> else {} if (= opText \"*\") let operation = clo<Number::multiply> else {} if (= opText \"/\") let operation = clo<Number::divide> else {} if (= opText \"%\") let operation = clo<Number::remainder> else {} if (= opText \"+\") let operation = clo<Number::add> else {} if (= opText \"-\") let operation = clo<Number::subtract> else {} if (= opText \"<<\") let operation = clo<Number::leftShift> else {} if (= opText \">>\") let operation = clo<Number::signedRightShift> else {} if (= opText \">>>\") let operation = clo<Number::unsignedRightShift> else {} if (= opText \"&\") let operation = clo<Number::bitwiseAND> else {} if (= opText \"^\") let operation = clo<Number::bitwiseXOR> else {} if (= opText \"|\") let operation = clo<Number::bitwiseOR> else {} } else if (? lnum: \"BigInt\") { if (= opText \"*\") let operation = clo<BigInt::multiply> else {} if (= opText \"+\") let operation = clo<BigInt::add> else {} if (= opText \"-\") let operation = clo<BigInt::subtract> else {} if (= opText \"<<\") let operation = clo<BigInt::leftShift> else {} if (= opText \">>\") let operation = clo<BigInt::signedRightShift> else {} if (= opText \"&\") let operation = clo<BigInt::bitwiseAND> else {} if (= opText \"^\") let operation = clo<BigInt::bitwiseXOR> else {} if (= opText \"|\") let operation = clo<BigInt::bitwiseOR> else {} } else { }",
"Let _privateName_ be the Private Name in _names_ whose [[Description]] is _privateIdentifier_.": "{ let idx = 0 loop[repeat] (< idx names.length) { let elem = names[idx] if (= elem.Description privateIdentifier) let privateName = elem else {} idx = (+ idx 1) } }",
"Let _privateName_ be the Private Name in _names_ whose [[Description]] is _privateIdentifier_.": "{ let idx = 0 while (< idx names.length) { let elem = names[idx] if (= elem.Description privateIdentifier) let privateName = elem else {} idx = (+ idx 1) } }",
"Let _realmRec_ be a new Realm Record.": "let realmRec = @REALM",
"Let _result_ be the Completion Record that is <emu-meta effects=\"user-code\">the result of evaluating</emu-meta> _F_ in a manner that conforms to the specification of _F_. If _thisArgument_ is ~uninitialized~, the *this* value is uninitialized; otherwise, _thisArgument_ provides the *this* value. _argumentsList_ provides the named parameters. _newTarget_ provides the NewTarget value.": "if (= thisArgument ~uninitialized~) call result = F.Code(undefined, argumentsList, newTarget) else call result = F.Code(thisArgument, argumentsList, undefined)",
"Let _sourceText_ be the source code of a script.": "let sourceText = @SOURCE_TEXT",
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/esmeta/cfg/Node.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ case class Branch(
var elseNode: Option[Node] = None,
) extends NodeWithInst {
def isLoop: Boolean = kind match
case BranchKind.If => false
case _ => true
case BranchKind.If => false
case BranchKind.While => true
}
enum BranchKind extends CFGElem:
case If
case Loop(str: String)
case While
4 changes: 2 additions & 2 deletions src/main/scala/esmeta/cfg/util/Stringifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class Stringifier(detail: Boolean, location: Boolean) {
given branchKindRule: Rule[BranchKind] = (app, kind) =>
import BranchKind.*
app >> (kind match {
case If => "if"
case Loop(str) => s"loop[$str]"
case If => "if"
case While => "while"
})
}
35 changes: 24 additions & 11 deletions src/main/scala/esmeta/cfgbuilder/CFGBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@ class CFGBuilder(
case (branch: Branch, true) => branch.thenNode = Some(to)
case (branch: Branch, false) => branch.elseNode = Some(to)

// aux branch case
def auxBranch(inst: Inst): Unit =
inst match
case branch: BranchInst =>
branch match
case IIf(cond, thenInst, elseInst) =>
val branch = Branch(nextNId, BranchKind.If, cond)
connect(branch.setInst(inst))
val thenPrev = {
prev = List((branch, true)); aux(thenInst); prev
}
val elsePrev = {
prev = List((branch, false)); aux(elseInst); prev
}
prev = thenPrev ++ elsePrev
case IWhile(cond, body) =>
val branch = Branch(nextNId, BranchKind.While, cond)
connect(branch.setInst(inst), isLoopPred = true)
prev = List((branch, true)); aux(body); connect(branch)
prev = List((branch, false))
case _ => throw Exception("impossible match")

// aux
def aux(inst: Inst): Unit = inst match {
case normal: NormalInst =>
Expand All @@ -59,17 +81,8 @@ class CFGBuilder(
block.insts += normal
prev = List((block, true))
case ISeq(insts) => for { i <- insts } aux(i)
case inst @ IIf(cond, thenInst, elseInst) =>
val branch = Branch(nextNId, BranchKind.If, cond)
connect(branch.setInst(inst))
val thenPrev = { prev = List((branch, true)); aux(thenInst); prev }
val elsePrev = { prev = List((branch, false)); aux(elseInst); prev }
prev = thenPrev ++ elsePrev
case inst @ ILoop(kind, cond, body) =>
val branch = Branch(nextNId, BranchKind.Loop(kind), cond)
connect(branch.setInst(inst), isLoopPred = true)
prev = List((branch, true)); aux(body); connect(branch)
prev = List((branch, false))
case branch: BranchInst =>
auxBranch(inst)
case callInst: CallInst =>
val call = Call(nextNId, callInst)
connect(call)
Expand Down
27 changes: 9 additions & 18 deletions src/main/scala/esmeta/compiler/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,7 @@ class Compiler(
case Param(name, _, Variadic) =>
List(
ILet(Name(name), EList(Nil)),
ILoop(
"args",
IWhile(
lessThan(EMath(BigDecimal(remaining, UNLIMITED)), argsLen),
IPush(EPop(ENAME_ARGS_LIST, true), toERef(Name(name)), false),
),
Expand Down Expand Up @@ -231,8 +230,7 @@ class Compiler(
IAssign(i, zero),
)
fb.addInst(
ILoop(
"foreach",
IWhile(
lessThan(iExpr, toStrERef(list, "length")),
fb.newScope {
fb.addInst(ILet(compile(x), toERef(list, iExpr)))
Expand All @@ -249,8 +247,7 @@ class Compiler(
IAssign(i, toStrERef(list, "length")),
)
fb.addInst(
ILoop(
"foreach",
IWhile(
lessThan(zero, iExpr),
fb.newScope {
fb.addInst(IAssign(i, sub(iExpr, one)))
Expand All @@ -264,8 +261,7 @@ class Compiler(
val (i, iExpr) = compileWithExpr(x)
fb.addInst(ILet(i, compile(fb, start)))
fb.addInst(
ILoop(
"foreach-int",
IWhile(
if (ascending) not(lessThan(compile(fb, end), iExpr))
else not(lessThan(iExpr, compile(fb, end))),
fb.newScope {
Expand All @@ -284,8 +280,7 @@ class Compiler(
IAssign(list, EKeys(toStrERef(compile(fb, obj), "SubMap"), intSorted)),
if (ascending) IAssign(i, zero)
else IAssign(i, toStrERef(list, "length")),
ILoop(
"repeat",
IWhile(
if (ascending) lessThan(iExpr, toStrERef(list, "length"))
else lessThan(zero, iExpr),
fb.newScope {
Expand All @@ -312,8 +307,7 @@ class Compiler(
IAssign(length, toStrERef(list, "length")),
)
fb.addInst(
ILoop(
"foreach-node",
IWhile(
lessThan(iExpr, lengthExpr),
fb.newScope {
fb.addInst(ILet(compile(x), toERef(list, iExpr)))
Expand All @@ -337,8 +331,7 @@ class Compiler(
fb.addInst(IPush(compile(fb, expr), ERef(compile(fb, ref)), true))
case RepeatStep(cond, body) =>
fb.addInst(
ILoop(
"repeat",
IWhile(
cond.fold(EBool(true))(compile(fb, _)),
compileWithScope(fb, body),
),
Expand Down Expand Up @@ -565,8 +558,7 @@ class Compiler(
IAssign(list, EList(Nil)),
)
fb.addInst(
ILoop(
"int-list",
IWhile(
if (asc) lessThan(iExpr, if (tInc) inc(tExpr) else tExpr)
else lessThan(if (fInc) dec(fExpr) else fExpr, iExpr),
fb.newScope {
Expand Down Expand Up @@ -922,8 +914,7 @@ class Compiler(
IAssign(l, list),
IAssign(i, zero),
IAssign(b, F),
ILoop(
"contains",
IWhile(
and(not(bExpr), lessThan(iExpr, toStrERef(l, "length"))),
fb.newScope {
fb.addInst(
Expand Down
Loading

0 comments on commit 97b2de4

Please sign in to comment.