Fix bugs, add cast builtin, bump version

This commit is contained in:
nickmqb 2020-10-06 18:54:45 +02:00
parent 0aeb7777a5
commit d7114cf681
10 changed files with 180 additions and 59 deletions

View file

@ -103,10 +103,10 @@ parseArgs(parser CommandLineArgsParser, isCompiler bool) {
if args.outputPath == "" && isCompiler {
parser.expected("--output [path]")
}
if !hasIndent && isCompiler {
if !hasIndent {
parser.expected("--indent [number]")
}
if !hasMaxErrors {
if !hasMaxErrors && isCompiler {
parser.expected("--max-errors [number]")
}

View file

@ -252,6 +252,7 @@ CallExpression struct #RefType {
BuiltinCall enum {
none
rep
cast_
slice
chunk
swizzle

View file

@ -44,7 +44,8 @@ EmulatorState struct #RefType {
tape List<Instruction>
resetProgram List<Instruction>
stepProgram List<Instruction>
propagateProgram List<Instruction>
updateProgram List<Instruction>
stack List<Value>
cycle long
@ -68,7 +69,8 @@ Emulator {
evalCtxField: -1,
evalCtxOutput: -1,
resetProgram: new List<Instruction>{},
stepProgram: new List<Instruction>{},
propagateProgram: new List<Instruction>{},
updateProgram: new List<Instruction>{},
stack: new List<Value>{},
cycle: -1,
}
@ -91,12 +93,14 @@ Emulator {
}
if s.comp.flags & CompilationFlags.simulate != 0 {
s.tape = s.stepProgram
s.tape = s.propagateProgram
for si in s.evalOrder {
if !s.infos[si].isStatic && !s.infos[si].isReg {
EmulatorStep.slot(s, si)
}
}
s.tape = s.updateProgram
for mi in s.moduleInstances {
EmulatorStep.module(s, mi)
assert(s.evalCtxField == -1 && s.evalCtxOutput == -1)
@ -113,7 +117,10 @@ Emulator {
step(s EmulatorState, clk string, val ulong) {
EmulatorRunner.setInput(s, s.moduleInstances[0], clk, val)
EmulatorRunner.run(s, s.stepProgram)
EmulatorRunner.run(s, s.propagateProgram)
EmulatorRunner.run(s, s.updateProgram)
Emulator.commitValues(s)
EmulatorRunner.run(s, s.propagateProgram)
}
commitValues(s EmulatorState) {
@ -142,7 +149,7 @@ EmulatorStep {
clock(s EmulatorState, st ClockStatement) {
token(s, st.name) // TODO: does not actually check edges, just looks at current state
if st.keyword.value != "posedge" {
emit(s, Opcode.invert)
emit(s, Opcode.not)
}
pc := emit(s, Opcode.jumpIfZero)
block(s, st.body)
@ -421,6 +428,9 @@ EmulatorStep {
if e.builtin == BuiltinCall.rep {
rep(s, e)
return
} else if e.builtin == BuiltinCall.cast_ {
cast_(s, e)
return
} else if e.builtin == BuiltinCall.slice {
slice(s, e)
return
@ -452,13 +462,36 @@ EmulatorStep {
}
}
cast_(s EmulatorState, e CallExpression) {
assert(s.evalCtxField == -1 && s.evalCtxOutput == -1)
fromTag := s.typeMap.get(e.args[0].expr)
tag := s.typeMap.get(e.args[1].expr)
assert(fromTag.kind == TagKind.number)
assert(tag.kind == TagKind.number && tag.q > 0)
expression(s, e.args[0].expr)
if fromTag.q == 0 {
if tag.q <= 64 {
mask(s, tag)
}
} else if tag.q < fromTag.q {
if tag.q <= 64 {
if fromTag.q > 64 {
emit(s, Opcode.toULong)
}
mask(s, tag)
} else {
abandon()
}
}
}
slice(s EmulatorState, e CallExpression) {
assert(s.evalCtxField == -1 && s.evalCtxOutput == -1)
dest := s.typeMap.get(e.args[0].expr)
target := s.typeMap.get(e.args[0].expr)
expression(s, e.args[0].expr)
expression(s, e.args[1].expr)
expression(s, e.args[2].expr)
emiti(s, Opcode.slice, TypeChecker.unpackWidth(dest))
emiti(s, Opcode.slice, TypeChecker.unpackWidth(target))
}
assignSlice(s EmulatorState, st AssignStatement, e CallExpression, si int) {

View file

@ -252,13 +252,18 @@ EmulatorOrderCalculator {
block(s EmulatorState, st Block) {
for n in st.contents {
match n {
ClockStatement: block(s, n.body)
ClockStatement: clock(s, n)
IfStatement: if_(s, n)
AssignStatement: assign(s, n)
}
}
}
clock(s EmulatorState, st ClockStatement) {
token(s, st.name)
block(s, st.body)
}
if_(s EmulatorState, st IfStatement) {
expression(s, st.expr)
block(s, st.ifBody)
@ -328,8 +333,9 @@ EmulatorOrderCalculator {
abandon()
}
} else if st.nameExpr.is(CallExpression) {
call := st.nameExpr.as(CallExpression)
if call.builtin == BuiltinCall.slice {
callExpr := st.nameExpr.as(CallExpression)
if callExpr.builtin == BuiltinCall.slice {
call(s, callExpr)
expression(s, st.expr)
} else {
abandon()

View file

@ -1,5 +1,5 @@
//tab_size=4
:compilerVersion = "0.1.1"
:compilerVersion = "0.1.2"
ParseCommaListState enum {
start
@ -671,6 +671,8 @@ Parser {
name := lhs.as(Token).value
if name == "rep" {
node.builtin = BuiltinCall.rep
} else if name == "cast" {
node.builtin = BuiltinCall.cast_
} else if name == "slice" {
node.builtin = BuiltinCall.slice
} else if name == "chunk" {

View file

@ -13,6 +13,7 @@ Opcode enum {
discard
neg
invert
not
add
sub
and
@ -30,6 +31,7 @@ Opcode enum {
slice
storeSlice
swizzle
toULong
toString(op Opcode) {
if op == Opcode.push {
@ -60,6 +62,8 @@ Opcode enum {
return "neg"
} else if op == Opcode.invert {
return "invert"
} else if op == Opcode.not {
return "not"
} else if op == Opcode.add {
return "add"
} else if op == Opcode.sub {
@ -94,6 +98,8 @@ Opcode enum {
return "storeSlice"
} else if op == Opcode.swizzle {
return "swizzle"
} else if op == Opcode.toULong {
return "toULong"
} else {
return "?"
}
@ -147,6 +153,8 @@ EmulatorRunner {
stack[top].z = -unpack(stack[top])
} else if ins.op == Opcode.invert {
stack[top].z = ~unpack(stack[top])
} else if ins.op == Opcode.not {
stack[top].z = unpack(stack[top]) == 0 ? 1_uL : 0
} else if ins.op == Opcode.add {
binaryOperator(s, stack, unpack(stack[lhs]) + unpack(stack[top]))
} else if ins.op == Opcode.sub {
@ -186,6 +194,8 @@ EmulatorRunner {
value := swizzle(s, stack[stack.count - 5], cast(unpack(stack[stack.count - 4]), int), cast(unpack(stack[stack.count - 3]), int), cast(unpack(stack[lhs]), int), cast(unpack(stack[top]), int))
stack.setCountChecked(stack.count - 5)
stack.add(value)
} else if ins.op == Opcode.toULong {
stack[top] = Value { z: unpackAsULong(stack[top]), kind: ValueKind.ulong_ }
} else {
abandon()
}

View file

@ -122,7 +122,7 @@ TypeChecker {
s.isStaticExpr = false
if etr.tag.kind != TagKind.unknown {
if def.type != null {
if canAssign(s, etr.tag, etr.value, dt) {
if canAssign_andUpgrade(s, etr.tag, etr.value, def.expr, dt) {
if etr.value.kind != ValueKind.none {
// OK
} else {
@ -200,7 +200,7 @@ TypeChecker {
s.errors.add(Error.at(s.unit, def.type.span, "Field type cannot be larger than $64"))
}
} else {
expectedFixedNumber(s, def.type)
expectedFixedNumberType(s, def.type)
}
}
}
@ -313,6 +313,17 @@ TypeChecker {
}
assign(s TypeCheckerState, st AssignStatement) {
assignSym(s, st)
if st.flags & AssignFlags.regUpdate != 0 {
if s.inClock {
// OK
} else {
s.errors.add(Error.at(s.unit, st.outKeyword != null ? st.outKeyword.span : RangeFinder.find(st.nameExpr), "Statement must be placed inside clock statement"))
}
}
}
assignSym(s TypeCheckerState, st AssignStatement) {
if st.flags & AssignFlags.typeCheckDone != 0 {
return
}
@ -335,11 +346,11 @@ TypeChecker {
// OK
} else if st.flags & AssignFlags.typeCheckStarted == 0 {
prev := pushContext(s, st.module.unit, st.module)
assign(s, st)
assignSym(s, st)
restoreContext(s, prev)
} else {
kind := (st.flags & AssignFlags.reg != 0) ? "register" : "wire"
s.errors.add(Error.at(s.unit, RangeFinder.find(reference), format("Recursive reference to {}", kind)))
s.errors.add(Error.at(s.unit, RangeFinder.find(reference), format("Type of {} cannot be inferred due to recursive reference, please specify the type explicity", kind)))
}
}
@ -373,11 +384,7 @@ TypeChecker {
} else if st.op.value == "<=" {
st.flags |= AssignFlags.regUpdate
if st.expr != null {
if s.inClock {
// OK
} else {
s.errors.add(Error.at(s.unit, st.outKeyword != null ? st.outKeyword.span : RangeFinder.find(st.nameExpr), "Statement must be placed inside clock statement"))
}
} else {
s.errors.add(Error.atIndex(s.unit, st.op.span.to, "Expected: expression"))
}
@ -393,17 +400,15 @@ TypeChecker {
if st.expr != null {
if st.type != null {
tag := typename(s, st, st.type)
if st.flags & AssignFlags.regUpdate != 0 {
// Mark as done so reg can be referenced by expression. This is fine as we have just determined the type of the register.
st.flags |= AssignFlags.typeCheckDone
}
s.isStaticExpr = st.flags & AssignFlags.regUpdate == 0
rhs := expressionWithGap(s, st.expr, st.type != null, tag)
s.isStaticExpr = false
if st.op != null && tag.isValid() {
if isFixedNumberOrStruct(tag) {
if rhs.tag.isValid() {
if canAssign(s, rhs.tag, rhs.value, tag) {
if canAssign_andUpgrade(s, rhs.tag, rhs.value, st.expr, tag) {
s.module.numRegSlots += numSlots(s.comp, tag)
} else {
badAssign(s, st.op, rhs.tag, tag)
@ -488,12 +493,14 @@ TypeChecker {
if st.expr != null {
if st.type != null {
tag := typename(s, st, st.type)
// Mark as done so reg can be referenced by expression. This is fine as we have just determined the type of the wire.
st.flags |= AssignFlags.typeCheckDone
s.isStaticExpr = st.flags & AssignFlags.static != 0
rhs := expressionWithGap(s, st.expr, st.type != null, tag)
s.isStaticExpr = false
if st.op != null && tag.isValid() {
if isFixedNumberOrStruct(tag) {
if canAssign(s, rhs.tag, rhs.value, tag) {
if canAssign_andUpgrade(s, rhs.tag, rhs.value, st.expr, tag) {
// OK
} else {
badAssign(s, st.op, rhs.tag, tag)
@ -547,10 +554,6 @@ TypeChecker {
}
regUpdate(s TypeCheckerState, st AssignStatement) {
if !s.inClock {
s.errors.add(Error.at(s.unit, st.outKeyword != null ? st.outKeyword.span : RangeFinder.find(st.nameExpr), "Statement must be placed inside clock statement"))
}
if s.module.blackboxKeyword != null {
statementNotAllowedInsideBlackbox(s, st.outKeyword != null ? st.outKeyword : st.nameExpr)
}
@ -587,7 +590,7 @@ TypeChecker {
}
if st.expr != null {
rhs := expressionWithGap(s, st.expr, true, tag)
if canAssign(s, rhs.tag, rhs.value, tag) {
if canAssign_andUpgrade(s, rhs.tag, rhs.value, st.expr, tag) {
// OK
} else {
badAssign(s, st.op, rhs.tag, tag)
@ -629,7 +632,7 @@ TypeChecker {
}
if st.expr != null {
rhs := expressionWithGap(s, st.expr, true, tag)
if canAssign(s, rhs.tag, rhs.value, tag) {
if canAssign_andUpgrade(s, rhs.tag, rhs.value, st.expr, tag) {
// OK
} else {
badAssign(s, st.op, rhs.tag, tag)
@ -826,13 +829,13 @@ TypeChecker {
unsupportedBinaryOp(s, e.op, lhs.tag, rhs.tag)
} else if lhs.tag.q > 0 || rhs.tag.q > 0 {
if lhs.tag.q == 0 {
if canConvertFreeConst(s, lhs.value, rhs.tag.q) {
if canConvertFreeConst_andUpgrade(s, lhs.value, e.lhs, rhs.tag) {
result.tag = Tag { kind: TagKind.number, q: rhs.tag.q }
} else {
badBinaryOperandConversion(s, RangeFinder.find(e.lhs), rhs.tag)
}
} else if rhs.tag.q == 0 {
if canConvertFreeConst(s, rhs.value, lhs.tag.q) {
if canConvertFreeConst_andUpgrade(s, rhs.value, e.rhs, lhs.tag) {
result.tag = Tag { kind: TagKind.number, q: lhs.tag.q }
} else {
badBinaryOperandConversion(s, RangeFinder.find(e.rhs), lhs.tag)
@ -874,13 +877,13 @@ TypeChecker {
unsupportedBinaryOp(s, e.op, lhs.tag, rhs.tag)
} else if lhs.tag.q > 0 || rhs.tag.q > 0 {
if lhs.tag.q == 0 {
if canConvertFreeConst(s, lhs.value, rhs.tag.q) {
if canConvertFreeConst_andUpgrade(s, lhs.value, e.lhs, rhs.tag) {
result.tag = Tag { kind: TagKind.number, q: 1 }
} else {
badBinaryOperandConversion(s, RangeFinder.find(e.lhs), rhs.tag)
}
} else if rhs.tag.q == 0 {
if canConvertFreeConst(s, rhs.value, lhs.tag.q) {
if canConvertFreeConst_andUpgrade(s, rhs.value, e.rhs, lhs.tag) {
result.tag = Tag { kind: TagKind.number, q: 1 }
} else {
badBinaryOperandConversion(s, RangeFinder.find(e.rhs), lhs.tag)
@ -971,14 +974,14 @@ TypeChecker {
fe := e.falseExpr != null ? expressionContinueGap(s, e.falseExpr) : TypeCheckResult{}
if s.gap && s.gapTag.kind == TagKind.number && s.gapTag.q > 0 {
if te.tag.isValid() {
if canAssign(s, te.tag, te.value, s.gapTag) {
if canAssign_andUpgrade(s, te.tag, te.value, e.trueExpr, s.gapTag) {
// OK
} else {
badConversion(s, e.trueExpr, te.tag, s.gapTag)
}
}
if fe.tag.isValid() {
if canAssign(s, fe.tag, fe.value, s.gapTag) {
if canAssign_andUpgrade(s, fe.tag, fe.value, e.falseExpr, s.gapTag) {
// OK
} else {
badConversion(s, e.falseExpr, fe.tag, s.gapTag)
@ -990,13 +993,13 @@ TypeChecker {
if te.tag.kind == TagKind.number && fe.tag.kind == TagKind.number {
if te.tag.q > 0 || fe.tag.q > 0 {
if te.tag.q == 0 {
if canConvertFreeConst(s, te.value, fe.tag.q) {
if canConvertFreeConst_andUpgrade(s, te.value, e.trueExpr, fe.tag) {
result.tag = Tag { kind: TagKind.number, q: fe.tag.q }
} else {
badBinaryOperandConversion(s, RangeFinder.find(e.trueExpr), fe.tag)
}
} else if fe.tag.q == 0 {
if canConvertFreeConst(s, fe.value, te.tag.q) {
if canConvertFreeConst_andUpgrade(s, fe.value, e.falseExpr, te.tag) {
result.tag = Tag { kind: TagKind.number, q: te.tag.q }
} else {
badBinaryOperandConversion(s, RangeFinder.find(e.falseExpr), te.tag)
@ -1073,7 +1076,7 @@ TypeChecker {
if vex.tag.isValid() {
if vex.tag.kind == TagKind.number && vex.value.kind == ValueKind.ulong_ {
if condValid {
if canAssign(s, vex.tag, vex.value, target.tag) {
if canAssign_andUpgrade(s, vex.tag, vex.value, c.valueExpr, target.tag) {
// OK
} else {
badConstConversion(s, c.valueExpr, vex.tag, target.tag)
@ -1203,6 +1206,8 @@ TypeChecker {
call(s TypeCheckerState, e CallExpression) {
if e.builtin == BuiltinCall.rep {
return rep(s, e)
} else if e.builtin == BuiltinCall.cast_ {
return cast_(s, e)
} else if e.builtin == BuiltinCall.slice {
return slice(s, e)
} else if e.builtin == BuiltinCall.chunk {
@ -1273,7 +1278,7 @@ TypeChecker {
rhs := expressionWithGap(s, arg.expr, true, tag)
s.allowDontCare = false
s.isStaticExpr = false
if !canAssign(s, rhs.tag, rhs.value, tag) {
if !canAssign_andUpgrade(s, rhs.tag, rhs.value, arg.expr, tag) {
badAssign(s, arg.colon, rhs.tag, tag)
}
if arg.name == null {
@ -1344,7 +1349,7 @@ TypeChecker {
if arg.expr != null {
rhs := expressionWithGap(s, arg.expr, true, tag)
if !canAssign(s, rhs.tag, rhs.value, tag) {
if !canAssign_andUpgrade(s, rhs.tag, rhs.value, arg.expr, tag) {
badAssign(s, arg.colon, rhs.tag, tag)
}
if arg.name == null {

View file

@ -29,6 +29,30 @@ TypeChecker {
return result
}
cast_(s TypeCheckerState, e CallExpression) {
if s.isStaticExpr {
// OK
} else if s.comp.flags & CompilationFlags.simulate == 0 {
s.errors.add(Error.at(s.unit, RangeFinder.find(e.target), "cast() can only be used in static initializer or in simulator"))
}
builtinArgs(s, e, 2)
target := numberArg(s, e, 0)
typeArg := fixedNumericTypeArg(s, e, 1)
result := TypeCheckResult{}
if target.tag.isValid() && typeArg.tag.isValid() {
result.tag = typeArg.tag
if typeArg.tag.q >= target.tag.q || typeArg.tag.q <= 64 {
// OK
} else {
s.errors.add(Error.at(s.unit, RangeFinder.find(e), "Unsupported cast; downcast not allowed for expressions larger than $64"))
}
if typeArg.tag.q >= target.tag.q && target.tag.q > 0 {
result.value = target.value
}
}
return result
}
slice(s TypeCheckerState, e CallExpression) {
if s.module == null {
s.errors.add(Error.at(s.unit, RangeFinder.find(e.target), "Cannot use slice() in constant initializer"))
@ -108,7 +132,7 @@ TypeChecker {
}
if st.expr != null {
rhs := expressionWithGap(s, st.expr, true, tag)
if canAssign(s, rhs.tag, rhs.value, tag) {
if canAssign_andUpgrade(s, rhs.tag, rhs.value, st.expr, tag) {
// OK
} else {
badAssign(s, st.op, rhs.tag, tag)
@ -259,4 +283,30 @@ TypeChecker {
}
return tr
}
fixedNumericTypeArg(s TypeCheckerState, e CallExpression, index int) {
if index >= e.args.count {
return TypeCheckResult{}
}
arg := e.args[index]
if arg.expr == null {
return TypeCheckResult{}
}
if arg.expr.is(Token) {
tag := typename(s, arg.expr, arg.expr.as(Token))
if tag.isValid() {
if tag.kind == TagKind.number && tag.q > 0 {
return TypeCheckResult { tag: tag }
} else {
expectedFixedNumberType(s, arg.expr)
}
}
} else {
tr := expression(s, arg.expr)
if tr.tag.isValid() {
expectedFixedNumberType(s, arg.expr)
}
}
return TypeCheckResult{}
}
}

View file

@ -29,10 +29,19 @@ TypeChecker {
return "[unknown]"
}
canAssign(s TypeCheckerState, from Tag, fromValue Value, to Tag) {
canAssign_andUpgrade(s TypeCheckerState, from Tag, fromValue Value, node Node, to Tag) {
if to.kind != TagKind.unknown {
if from.kind == TagKind.number {
return to.kind == TagKind.number && (to.q == from.q || (from.q == 0 && canConvertFreeConst(s, fromValue, to.q)))
if to.kind == TagKind.number {
assert(to.q > 0)
if to.q == from.q {
return true
}
if from.q == 0 {
return canConvertFreeConst_andUpgrade(s, fromValue, node, to)
}
}
return false
} else if from.kind == TagKind.moduleOut || from.kind == TagKind.struct_ {
return to.kind == from.kind && to.q == from.q
}
@ -51,9 +60,10 @@ TypeChecker {
return result
}
canConvertFreeConst(s TypeCheckerState, val Value, width int) {
if val.kind == ValueKind.ulong_ {
return highestBit(val.z) <= width //|| highestBit(~val.z) < width
canConvertFreeConst_andUpgrade(s TypeCheckerState, fromValue Value, node Node, to Tag) {
if fromValue.kind == ValueKind.ulong_ && highestBit(fromValue.z) <= to.q {
s.typeMap.update(node, to)
return true
}
return false
}
@ -163,7 +173,7 @@ TypeChecker {
s.errors.add(Error.at(s.unit, op.span, format("Cannot apply operator {} to argument of type {} and target of type {}", op.value, tagString(s.comp, arg), tagString(s.comp, target))))
}
expectedFixedNumber(s TypeCheckerState, e Node) {
expectedFixedNumberType(s TypeCheckerState, e Node) {
s.errors.add(Error.at(s.unit, RangeFinder.find(e), "Expected: fixed width numeric type"))
}

View file

@ -72,6 +72,7 @@ VerilogGenerator {
s.globals.add("output")
s.globals.add("inout")
s.globals.add("case")
s.globals.add("signed")
for u in s.comp.units {
for node in u.contents {
@ -785,19 +786,22 @@ VerilogGenerator {
value(s GeneratorState, tag Tag, value Value) {
s.isNonIndexable = true
assert(tag.kind == TagKind.number)
if value.kind == ValueKind.ulong_ {
if tag.q > 0 {
write(s, format("{}'d{}", tag.q, value.z))
} else {
write(s, format("{}", value.z))
if tag.q == 0 {
write(s, "(((compiler_bug:invalid_num")
}
if value.kind == ValueKind.ulong_ {
write(s, format("{}'d{}", tag.q, value.z))
} else if value.kind == ValueKind.byteArray {
data := EmulatorRunner.unpackArray(value)
write(s, format("{}'h", tag.q))
from := min((tag.q + 7) / 8, data.count)
if from > 0 {
for i := from - 1; i >= 0; i -= 1 {
Util.writeByteHexTo(data[i], s.out.sb)
}
} else {
write(s, "0")
}
} else {
abandon()
}