Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions languages/lang0.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ arith ::= (Neg <type> <expr>)
| (Shl <type> <expr> <expr>)
| (Shr <type> <expr> <expr>)

conv ::= (Reinterp <type> <type> <expr>)
| (Conv <type> <type> <expr>)

# TODO: `Reinterp` and `Conv` need to be replaced with concrete operations
# (e.g., zero extend, sign extend, ftoi.)
conv ::= (Reinterp <type> <type> <expr>) # bit casting (between numeric types)
| (Conv <type> <type> <expr>) # int-to-float conversion and vice versa
| (Zext <type> <type> <expr>) # zero extension (integer only)
| (Sext <type> <type> <expr>) # sign extension (integer only)
| (Trunc <type> <type> <expr>) # integer truncation
| (Demote <type> <type> <expr>) # larger float to smaller float demotion
| (Promote <type> <type> <expr>) # smaller float to larger float promotion

memops ::= (Load <type> <expr>)

Expand Down
67 changes: 40 additions & 27 deletions passes/pass0.nim
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ proc genCheckedBinary(c; tree; op: NodeIndex, opc: Opcode) =
c.instr(opc, int8(parseType(tree, typ).size * 8))
c.instr(opcPopLocal, tree[op, 3].id)

proc prepareConvOp(c; tree; n: NodeIndex): (Type0, Type0) =
let (dst, src, x) = triplet(tree, n)
c.genExpr(tree, x)
(parseType(tree, dst), parseType(tree, src))

proc loadLocal(c; local: int32) =
c.instr(opcGetLocal, local)
# TODO: merge opcPopLocal + opcGetLocal into opcSetLocal, if there's no
Expand Down Expand Up @@ -359,43 +364,51 @@ proc genExpr(c; tree; val: NodeIndex) =
of 4: c.instr(opcReinterpF32)
else: unreachable()
of Conv:
# numeric conversion
let
dtyp = parseType(tree, tree.child(val, 0))
styp = parseType(tree, tree.child(val, 1))
(dtyp, styp) = c.prepareConvOp(tree, val)
width = int8(dtyp.size * 8)

c.genExpr(tree, tree.child(val, 2))
case dtyp.kind
of t0kInt:
case styp.kind
of t0kInt, t0kUInt:
c.signExtend(dtyp)
of t0kFloat:
c.instr(opcFloatToSInt, int8(dtyp.size * 8))
assert styp.kind == t0kFloat
c.instr(opcFloatToSInt, width)
of t0kUInt:
case styp.kind
of t0kInt:
c.mask(dtyp)
of t0kUInt:
# the upper bits can only be set if the source type has larger bit-
# width than the destination
if dtyp.size < styp.size:
c.mask(dtyp)
of t0kFloat:
c.instr(opcFloatToUInt, int8(dtyp.size * 8))
assert styp.kind == t0kFloat
c.instr(opcFloatToUInt, width)
of t0kFloat:
case styp.kind
of t0kInt:
c.instr(opcSIntToFloat, int8(dtyp.size * 8))
c.instr(opcSIntToFloat, width)
of t0kUInt:
c.instr(opcUIntToFloat, int8(dtyp.size * 8))
c.instr(opcUIntToFloat, width)
of t0kFloat:
if styp.size == 8 and dtyp.size == 4:
# demote 64-bit float to 32-bit float value. Reinterpret the bits as
# a 32-bit integer (which internally converts to a 32-bit float
# first) and then reinterpret the result as a 32-bit float
c.instr(opcReinterpI32)
c.instr(opcReinterpF32)
unreachable()
# TODO: demote the result when the destination type is a 32-bit float
of Sext:
let (dtyp, styp) = c.prepareConvOp(tree, val)
c.signExtend(styp)
if dtyp.kind == t0kUInt:
c.mask(dtyp)
of Zext:
let (_, styp) = c.prepareConvOp(tree, val)
if styp.kind == t0kInt:
c.mask(styp) # zero all leading bits
of Trunc:
let (dtyp, _) = c.prepareConvOp(tree, val)
if dtyp.kind == t0kInt:
c.signExtend(dtyp)
else:
c.mask(dtyp)
of Demote:
# demote 64-bit float to 32-bit float value. Reinterpret the bits as
# a 32-bit integer (which internally converts to a 32-bit float
# first) and then reinterpret the result as a 32-bit float
c.genExpr(tree, tree.child(val, 2))
c.instr(opcReinterpI32)
c.instr(opcReinterpF32)
of Promote:
c.genExpr(tree, tree.child(val, 2))
# a no-op
of Call:
c.genCall(tree, val, 0, ^1)
else:
Expand Down
5 changes: 3 additions & 2 deletions passes/pass_aggregateParams.nim
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ proc signature(c; tree; n): NodeIndex =
proc typeof(c; tree; n): Node =
case tree[n].kind
of Path, Load, Deref, Add, Sub, Mul, Div, Mod, BitNot, BitAnd, BitOr, BitXor,
AddChck, SubChck, MulChck, Shl, Shr, Conv, Reinterp, Neg:
AddChck, SubChck, MulChck, Shl, Shr, Conv, Zext, Sext, Trunc, Demote,
Promote, Reinterp, Neg:
# these are all expression that store their result type as the first
# node
tree[n, 0]
Expand Down Expand Up @@ -235,7 +236,7 @@ proc lowerExpr(c; tree; n, bu) =
c.lowerExpr(tree, tree.child(n, 0), bu)
of Load, Neg, BitNot:
c.lowerExpr(tree, tree.child(n, 1), bu)
of Reinterp, Conv:
of Reinterp, Conv, Zext, Sext, Trunc, Demote, Promote:
c.lowerExpr(tree, tree.child(n, 2), bu)
of Add, Sub, Mul, Div, Mod, BitAnd, BitOr, BitXor, AddChck, SubChck, MulChck,
Eq, Lt, Le, Shl, Shr:
Expand Down
2 changes: 1 addition & 1 deletion passes/syntax.nim
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type
BitAnd, BitNot, BitXor, BitOr
Shl, Shr

Conv, Reinterp
Conv, Reinterp, Zext, Sext, Trunc, Demote, Promote

Goto, Loop, Raise, Unreachable, Select, Branch
CheckedCall, CheckedCallAsgn, Unwind, Choice
Expand Down
139 changes: 84 additions & 55 deletions skully/backend.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1055,11 +1055,75 @@ proc genLength(c; env: var MirEnv; tree; n; dest: Expr, stmts) =
else:
unreachable()

proc genNumericConv(c; env; val: Expr, to: TypeId, bu) =
## If the types are equal (at the IL level), emits an *unchecked* numeric
## conversion for `val` to type `to`.
proc skip(env; t: TypeId): TypeId =
result = env.types.canonical(t)
# treat imported types as their underlying representation
if env.types.headerFor(t, Lowered).kind == tkImported:
result = env.skip(env.types.headerFor(t, Lowered).elem)

let dst = env.types.headerFor(env.skip(to), Lowered)
let src = env.types.headerFor(env.skip(val.typ), Lowered)

template convOp(kind: NodeKind) =
bu.subTree kind:
bu.add typeRef(c, env, to)
bu.add typeRef(c, env, val.typ)
bu.use val

template sizedConv(widen, narrow: NodeKind) =
if dst.size(env.types) < src.size(env.types):
convOp narrow
elif dst.size(env.types) > dst.size(env.types):
convOp widen
else:
# nothing to do
bu.use val

const
uints = {tkUInt, tkChar, tkBool}
ints = {tkInt}

case dst.kind
of uints:
case src.kind
of ints, uints:
# conversion to uint is the same as casting to uint
sizedConv Trunc, Zext
of tkFloat:
convOp Conv
else:
unreachable(src.kind)
of ints:
case src.kind
of ints: sizedConv Trunc, Sext
of uints: sizedConv Trunc, Zext
of tkFloat: convOp Conv
else:
unreachable(src.kind)
of tkFloat:
case src.kind
of ints, uints:
convOp Conv
of tkFloat:
sizedConv Promote, Demote
else:
unreachable()
of tkPtr, tkPointer, tkProc, tkCstring:
# a conversion between pointer types is a no-op. These conversion aren't
# really numeric conversions, but it's easier to also include them here
bu.use val
else:
unreachable(dst.kind)

proc genSetElem(c; env; tree; n; styp: TypeId, bu) =
## Emits the expression for loading a value for use in a set-related
## operation. This means turning the value into one relative to the start of
## the set's value range. The resulting value is always of uint16 type.
proc aux(c; env; tree; n; styp: TypeId, bu) =
let val = makeExpr tree[n].typ:
# shift the operand's value so that its range starts at 0
assert env.types[styp].kind == tySet
let first = c.graph.config.firstOrd(env.types[styp])
if first != Zero:
Expand All @@ -1070,16 +1134,8 @@ proc genSetElem(c; env; tree; n; styp: TypeId, bu) =
else:
c.translateValue(env, tree, n, true, bu)

let typ = env.types.canonical(tree[n].typ)
let desc = env.types.headerFor(typ, Lowered)
if desc.kind != tkUInt or desc.size(env.types) != 2:
# sets cannot have more than 2^16 elements, hence a uint16
bu.subTree Conv:
bu.add node(UInt, 2)
bu.add typeRef(c, env, typ)
aux(c, env, tree, n, styp, bu)
else:
aux(c, env, tree, n, styp, bu)
# sets cannot have more than 2^16 elements, hence a uint16
c.genNumericConv(env, val, UInt16Type, bu)

proc genSetOp(c; env: var MirEnv, tree; n; dest: Expr, stmts) =
## Generates and emits the IL code for a binary set operation.
Expand Down Expand Up @@ -1381,10 +1437,10 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) =
wrapAsgn:
value(tree.argument(n, 0))
of mChr:
wrapAsgn Conv:
bu.add typeRef(c, env, tree[n].typ)
bu.add typeRef(c, env, tree[tree.argument(n, 0)].typ)
value(tree.argument(n, 0))
# it's a conversion, nothing more
let input = c.gen(env, tree, NodePosition(tree.argument(n, 0)), true)
wrapAsgn:
c.genNumericConv(env, input, tree[n].typ, bu)
of mIncl, mExcl, mLtSet, mLeSet, mEqSet, mMinusSet, mPlusSet, mMulSet,
mInSet:
c.genSetOp(env, tree, n, dest, stmts)
Expand All @@ -1408,7 +1464,7 @@ proc genMagic(c; env: var MirEnv, tree; n; dest: Expr, stmts) =
# also use countBits32, but widen the operand first
wrapAsgn Call:
bu.add compilerProc(c, env, "countBits32")
bu.subTree Conv:
bu.subTree Zext:
bu.add node(UInt, 4)
bu.add typeRef(c, env, tree[a].typ)
value a
Expand Down Expand Up @@ -1934,10 +1990,11 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) =
wrapAsgn:
value(n)
of mnkConv, mnkStdConv:
wrapAsgn Conv:
bu.add typeRef(c, env, tree[n].typ)
bu.add typeRef(c, env, tree[n, 0].typ)
value(tree.child(n, 0))
# the high-level MIR conversions are lowered into the more specific
# operations of the target IL
let input = c.gen(env, tree, tree.child(n, 0), true)
wrapAsgn:
c.genNumericConv(env, input, tree[n].typ, bu)
of mnkCopy, mnkMove, mnkSink:
wrapAsgn:
value(tree.child(n, 0))
Expand Down Expand Up @@ -2254,10 +2311,6 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) =
takeAddr tree.child(n, 0)
bu.add node(IntVal, c.lit.pack(size))
else:
template isUnsigned(id: TypeId): bool =
env.types.headerFor(id, Lowered).kind in
{tkPtr, tkPointer, tkRef, tkUInt, tkChar, tkBool}

let a = env.types.headerFor(dst, Lowered).size(env.types)
let b = env.types.headerFor(src, Lowered).size(env.types)
# for the implementation, keep in mind that Reinterp only supports
Expand All @@ -2271,40 +2324,16 @@ proc translateExpr(c; env: var MirEnv, tree; n; dest: Expr, stmts) =
bu.add typeRef(c, env, dst)
bu.add typeRef(c, env, src)
value tree.child(n, 0)
elif isUnsigned(dst):
if isUnsigned(src):
# a simple conversion (i.e., a zero extension) is enough
wrapAsgn Conv:
bu.add typeRef(c, env, dst)
bu.add typeRef(c, env, src)
value tree.child(n, 0)
else:
wrapAsgn Conv:
bu.add typeRef(c, env, dst)
bu.add node(UInt, b.uint32)
bu.subTree Reinterp:
bu.add node(UInt, b.uint32)
bu.add typeRef(c, env, src)
value tree.child(n, 0)
elif isUnsigned(src):
wrapAsgn Reinterp:
elif a < b:
wrapAsgn Trunc:
bu.add typeRef(c, env, dst)
bu.add node(UInt, a.uint32)
bu.subTree Conv:
bu.add node(UInt, a.uint32)
bu.add typeRef(c, env, src)
value tree.child(n, 0)
bu.add typeRef(c, env, src)
value tree.child(n, 0)
else:
wrapAsgn Reinterp:
wrapAsgn Zext:
bu.add typeRef(c, env, dst)
bu.add node(UInt, a.uint32)
bu.subTree Conv:
bu.add node(UInt, a.uint32)
bu.add node(UInt, b.uint32)
bu.subTree Reinterp:
bu.add node(UInt, b.uint32)
bu.add typeRef(c, env, src)
value tree.child(n, 0)
bu.add typeRef(c, env, src)
value tree.child(n, 0)
else:
unreachable()

Expand Down
12 changes: 12 additions & 0 deletions tests/pass0/t03_demote_float64_to_float32.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
discard """
output: "(Done 0.5)"
"""
(TypeDefs
(ProcTy (Float 4)))
(GlobalDefs)
(ProcDefs
(ProcDef (Type 0) 0 (Locals)
(List
(Block (Params)
(Return
(Demote (Float 4) (Float 8) (FloatVal 0.5000000149)))))))
5 changes: 5 additions & 0 deletions tests/pass0/t03_promote_float64_to_float32.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.type t0 () -> float
.start t0 p0
LdImmFloat 0.5
Ret
.end
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ discard """
(List
(Block (Params)
(Return
(Conv (Float 4) (Float 8) (FloatVal 0.5000000149)))))))
(Promote (Float 8) (Float 4) (FloatVal 0.5)))))))
6 changes: 6 additions & 0 deletions tests/pass0/t03_sext_int_to_int.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.type t0 () -> int
.start t0 p0
LdImmInt -3
SignExtend 56
Ret
.end
12 changes: 12 additions & 0 deletions tests/pass0/t03_sext_int_to_int.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
discard """
output: "(Done (18446744073709551613 or -3))"
"""
(TypeDefs
(ProcTy (Int 2)))
(GlobalDefs)
(ProcDefs
(ProcDef (Type 0) 0 (Locals)
(List
(Block (Params)
(Return
(Sext (Int 2) (Int 1) (IntVal -3)))))))
7 changes: 7 additions & 0 deletions tests/pass0/t03_sext_int_to_uint.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.type t0 () -> int
.start t0 p0
LdImmInt -3
SignExtend 56
Mask 16
Ret
.end
12 changes: 12 additions & 0 deletions tests/pass0/t03_sext_int_to_uint.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
discard """
output: "(Done 65533)"
"""
(TypeDefs
(ProcTy (UInt 2)))
(GlobalDefs)
(ProcDefs
(ProcDef (Type 0) 0 (Locals)
(List
(Block (Params)
(Return
(Sext (UInt 2) (Int 1) (IntVal -3)))))))
Loading