summary refs log tree commit diff stats
path: root/compiler/lowerings.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/lowerings.nim')
-rw-r--r--compiler/lowerings.nim370
1 files changed, 370 insertions, 0 deletions
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
new file mode 100644
index 000000000..2c9c4cb32
--- /dev/null
+++ b/compiler/lowerings.nim
@@ -0,0 +1,370 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements common simple lowerings.
+
+const
+  genPrefix* = ":tmp"         # prefix for generated names
+
+import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
+  lineinfos
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+proc newDeref*(n: PNode): PNode {.inline.} =
+  result = newNodeIT(nkHiddenDeref, n.info, n.typ.elementType)
+  result.add n
+
+proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
+  if tup.kind == nkHiddenAddr:
+    result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent}))
+    result.add newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent})[i])
+    result[0].add tup[0]
+    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
+    lit.intVal = i
+    result[0].add lit
+  else:
+    result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
+                       abstractInst)[i])
+    result.add copyTree(tup)
+    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
+    lit.intVal = i
+    result.add lit
+
+proc addVar*(father, v: PNode) =
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = v
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = vpart[1]
+  father.add vpart
+
+proc addVar*(father, v, value: PNode) =
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = v
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = value
+  father.add vpart
+
+proc newAsgnStmt*(le, ri: PNode): PNode =
+  result = newNodeI(nkAsgn, le.info, 2)
+  result[0] = le
+  result[1] = ri
+
+proc newFastAsgnStmt*(le, ri: PNode): PNode =
+  result = newNodeI(nkFastAsgn, le.info, 2)
+  result[0] = le
+  result[1] = ri
+
+proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
+  result = newNodeI(nkFastAsgn, le.info, 2)
+  result[0] = le
+  result[1] = newNodeIT(nkCall, ri.info, ri.typ)
+  result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove))
+  result[1].add ri
+
+proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
+  assert n.kind == nkVarTuple
+  let value = n.lastSon
+  result = newNodeI(nkStmtList, n.info)
+
+  var tempAsNode: PNode
+  let avoidTemp = value.kind == nkSym
+  if avoidTemp:
+    tempAsNode = value
+  else:
+    var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
+                  owner, value.info, g.config.options)
+    temp.typ = skipTypes(value.typ, abstractInst)
+    incl(temp.flags, sfFromGeneric)
+    tempAsNode = newSymNode(temp)
+
+  var v = newNodeI(nkVarSection, value.info)
+  if not avoidTemp:
+    v.addVar(tempAsNode, value)
+  result.add(v)
+
+  for i in 0..<n.len-2:
+    let val = newTupleAccess(g, tempAsNode, i)
+    if n[i].kind == nkSym: v.addVar(n[i], val)
+    else: result.add newAsgnStmt(n[i], val)
+
+proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): PNode =
+  ## Turns (value) into (let tmp = value; tmp) so that 'value' can be re-used
+  ## freely, multiple times. This is frequently required and such a builtin would also be
+  ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
+  result = newNodeIT(nkStmtListExpr, value.info, value.typ)
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
+                    owner, value.info, g.config.options)
+  temp.typ = skipTypes(value.typ, abstractInst)
+  incl(temp.flags, sfFromGeneric)
+
+  var v = newNodeI(nkLetSection, value.info)
+  let tempAsNode = newSymNode(temp)
+  v.addVar(tempAsNode)
+  result.add(v)
+  result.add newAsgnStmt(tempAsNode, value)
+  result.add tempAsNode
+
+proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
+  result = newNodeI(nkBracketExpr, tup.info)
+  result.add copyTree(tup)
+  var lit = newNodeI(nkIntLit, tup.info)
+  lit.intVal = i
+  result.add lit
+
+proc newTryFinally*(body, final: PNode): PNode =
+  result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
+
+proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
+  result = newNodeI(nkStmtList, n.info)
+  # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
+  var temp = newSym(skVar, getIdent(g.cache, genPrefix), idgen, owner, n.info, owner.options)
+  temp.typ = n[1].typ
+  incl(temp.flags, sfFromGeneric)
+  incl(temp.flags, sfGenSym)
+
+  var v = newNodeI(nkVarSection, n.info)
+  let tempAsNode = newSymNode(temp)
+
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = tempAsNode
+  vpart[1] = newNodeI(nkEmpty, v.info)
+  vpart[2] = n[1]
+  v.add vpart
+
+  result.add(v)
+  result.add newFastAsgnStmt(n[1], n[2])
+  result.add newFastAsgnStmt(n[2], tempAsNode)
+
+proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
+  result = newType(tyObject, idgen, owner)
+  if final:
+    rawAddSon(result, nil)
+    incl result.flags, tfFinal
+  else:
+    rawAddSon(result, getCompilerProc(g, "RootObj").typ)
+  result.n = newNodeI(nkRecList, info)
+  let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
+                  idgen, owner, info, owner.options)
+  incl s.flags, sfAnon
+  s.typ = result
+  result.sym = s
+
+template fieldCheck {.dirty.} =
+  when false:
+    if tfCheckedForDestructor in obj.flags:
+      echo "missed field ", field.name.s
+      writeStackTrace()
+
+proc rawAddField*(obj: PType; field: PSym) =
+  assert field.kind == skField
+  field.position = obj.n.len
+  obj.n.add newSymNode(field)
+  propagateToOwner(obj, field.typ)
+  fieldCheck()
+
+proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
+  # returns a[].field as a node
+  assert field.kind == skField
+  var deref = newNodeI(nkHiddenDeref, info)
+  deref.typ = a.typ.skipTypes(abstractInst)[0]
+  deref.add a
+  result = newNodeI(nkDotExpr, info)
+  result.add deref
+  result.add newSymNode(field)
+  result.typ = field.typ
+
+proc rawDirectAccess*(obj, field: PSym): PNode =
+  # returns a.field as a node
+  assert field.kind == skField
+  result = newNodeI(nkDotExpr, field.info)
+  result.add newSymNode(obj)
+  result.add newSymNode(field)
+  result.typ = field.typ
+
+proc lookupInRecord(n: PNode, id: ItemId): PSym =
+  result = nil
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      result = lookupInRecord(n[i], id)
+      if result != nil: return
+  of nkRecCase:
+    if n[0].kind != nkSym: return
+    result = lookupInRecord(n[0], id)
+    if result != nil: return
+    for i in 1..<n.len:
+      case n[i].kind
+      of nkOfBranch, nkElse:
+        result = lookupInRecord(lastSon(n[i]), id)
+        if result != nil: return
+      else: discard
+  of nkSym:
+    if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
+  else: discard
+
+proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
+  # because of 'gensym' support, we have to mangle the name with its ID.
+  # This is hacky but the clean solution is much more complex than it looks.
+  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
+                     idgen, s.owner, s.info, s.options)
+  field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+  let t = skipIntLit(s.typ, idgen)
+  field.typ = t
+  if s.kind in {skLet, skVar, skField, skForVar}:
+    #field.bitsize = s.bitsize
+    field.alignment = s.alignment
+  assert t.kind != tyTyped
+  propagateToOwner(obj, t)
+  field.position = obj.n.len
+  # sfNoInit flag for skField is used in closureiterator codegen
+  field.flags = s.flags * {sfCursor, sfNoInit}
+  obj.n.add newSymNode(field)
+  fieldCheck()
+  result = field
+
+proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
+  result = lookupInRecord(obj.n, s.itemId)
+  if result == nil:
+    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), idgen,
+                       s.owner, s.info, s.options)
+    field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
+    let t = skipIntLit(s.typ, idgen)
+    field.typ = t
+    assert t.kind != tyTyped
+    propagateToOwner(obj, t)
+    field.position = obj.n.len
+    obj.n.add newSymNode(field)
+    result = field
+
+proc newDotExpr*(obj, b: PSym): PNode =
+  result = newNodeI(nkDotExpr, obj.info)
+  let field = lookupInRecord(obj.typ.n, b.itemId)
+  assert field != nil, b.name.s
+  result.add newSymNode(obj)
+  result.add newSymNode(field)
+  result.typ = field.typ
+
+proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
+  # returns a[].b as a node
+  var deref = newNodeI(nkHiddenDeref, info)
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
+  var t = deref.typ.skipTypes(abstractInst)
+  var field: PSym
+  while true:
+    assert t.kind == tyObject
+    field = lookupInRecord(t.n, b)
+    if field != nil: break
+    t = t.baseClass
+    if t == nil: break
+    t = t.skipTypes(skipPtrs)
+  #if field == nil:
+  #  echo "FIELD ", b
+  #  debug deref.typ
+  assert field != nil
+  deref.add a
+  result = newNodeI(nkDotExpr, info)
+  result.add deref
+  result.add newSymNode(field)
+  result.typ = field.typ
+
+proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
+  # returns a[].b as a node
+  var deref = newNodeI(nkHiddenDeref, info)
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
+  var t = deref.typ.skipTypes(abstractInst)
+  var field: PSym
+  let bb = getIdent(cache, b)
+  while true:
+    assert t.kind == tyObject
+    field = getSymFromList(t.n, bb)
+    if field != nil: break
+    t = t.baseClass
+    if t == nil: break
+    t = t.skipTypes(skipPtrs)
+  #if field == nil:
+  #  echo "FIELD ", b
+  #  debug deref.typ
+  assert field != nil
+  deref.add a
+  result = newNodeI(nkDotExpr, info)
+  result.add deref
+  result.add newSymNode(field)
+  result.typ = field.typ
+
+proc getFieldFromObj*(t: PType; v: PSym): PSym =
+  assert v.kind != skField
+  var t = t
+  while true:
+    assert t.kind == tyObject
+    result = lookupInRecord(t.n, v.itemId)
+    if result != nil: break
+    t = t.baseClass
+    if t == nil: break
+    t = t.skipTypes(skipPtrs)
+
+proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
+  # returns a[].b as a node
+  result = indirectAccess(a, b.itemId, info)
+
+proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
+  result = indirectAccess(newSymNode(a), b, info)
+
+proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
+  result = newNodeI(nkAddr, n.info, 1)
+  result[0] = n
+  result.typ = newType(typeKind, idgen, n.typ.owner)
+  result.typ.rawAddSon(n.typ)
+
+proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
+  result = newNodeIT(k, n.info,
+                     n.typ.skipTypes(abstractInst).elementType)
+  result.add n
+
+proc callCodegenProc*(g: ModuleGraph; name: string;
+                      info: TLineInfo = unknownLineInfo;
+                      arg1: PNode = nil, arg2: PNode = nil,
+                      arg3: PNode = nil, optionalArgs: PNode = nil): PNode =
+  result = newNodeI(nkCall, info)
+  let sym = magicsys.getCompilerProc(g, name)
+  if sym == nil:
+    localError(g.config, info, "system module needs: " & name)
+  else:
+    result.add newSymNode(sym)
+    if arg1 != nil: result.add arg1
+    if arg2 != nil: result.add arg2
+    if arg3 != nil: result.add arg3
+    if optionalArgs != nil:
+      for i in 1..<optionalArgs.len-2:
+        result.add optionalArgs[i]
+    result.typ = sym.typ.returnType
+
+proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
+  result = nkIntLit.newIntNode(value)
+  result.typ = getSysType(g, info, tyInt)
+
+proc genHigh*(g: ModuleGraph; n: PNode): PNode =
+  if skipTypes(n.typ, abstractVar).kind == tyArray:
+    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
+  else:
+    result = newNodeI(nkCall, n.info, 2)
+    result.typ = getSysType(g, n.info, tyInt)
+    result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
+    result[1] = n
+
+proc genLen*(g: ModuleGraph; n: PNode): PNode =
+  if skipTypes(n.typ, abstractVar).kind == tyArray:
+    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
+  else:
+    result = newNodeI(nkCall, n.info, 2)
+    result.typ = getSysType(g, n.info, tyInt)
+    result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
+    result[1] = n
+