diff --git a/compiler/args_parser.mu b/compiler/args_parser.mu index 752b739..8a1b7cc 100644 --- a/compiler/args_parser.mu +++ b/compiler/args_parser.mu @@ -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]") } diff --git a/compiler/ast.mu b/compiler/ast.mu index d1c48b9..f717a5b 100644 --- a/compiler/ast.mu +++ b/compiler/ast.mu @@ -252,6 +252,7 @@ CallExpression struct #RefType { BuiltinCall enum { none rep + cast_ slice chunk swizzle diff --git a/compiler/emulator.mu b/compiler/emulator.mu index 432c2c5..e1041ae 100644 --- a/compiler/emulator.mu +++ b/compiler/emulator.mu @@ -44,7 +44,8 @@ EmulatorState struct #RefType { tape List resetProgram List - stepProgram List + propagateProgram List + updateProgram List stack List cycle long @@ -68,7 +69,8 @@ Emulator { evalCtxField: -1, evalCtxOutput: -1, resetProgram: new List{}, - stepProgram: new List{}, + propagateProgram: new List{}, + updateProgram: new List{}, stack: new List{}, 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) { diff --git a/compiler/emulator_init.mu b/compiler/emulator_init.mu index e05a469..67e4015 100644 --- a/compiler/emulator_init.mu +++ b/compiler/emulator_init.mu @@ -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() diff --git a/compiler/parser.mu b/compiler/parser.mu index 3018d06..1721cef 100644 --- a/compiler/parser.mu +++ b/compiler/parser.mu @@ -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" { diff --git a/compiler/tape.mu b/compiler/tape.mu index 0cc1748..224d02c 100644 --- a/compiler/tape.mu +++ b/compiler/tape.mu @@ -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() } diff --git a/compiler/type_checker.mu b/compiler/type_checker.mu index faaf947..689b346 100644 --- a/compiler/type_checker.mu +++ b/compiler/type_checker.mu @@ -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")) - } + // OK } 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 - } + // 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 { diff --git a/compiler/type_checker_builtins.mu b/compiler/type_checker_builtins.mu index 52ebaad..433b514 100644 --- a/compiler/type_checker_builtins.mu +++ b/compiler/type_checker_builtins.mu @@ -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{} + } } diff --git a/compiler/type_checker_utils.mu b/compiler/type_checker_utils.mu index 3d17eec..8a57f8a 100644 --- a/compiler/type_checker_utils.mu +++ b/compiler/type_checker_utils.mu @@ -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")) } diff --git a/compiler/verilog_generator.mu b/compiler/verilog_generator.mu index 0a7440a..9fe4252 100644 --- a/compiler/verilog_generator.mu +++ b/compiler/verilog_generator.mu @@ -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,18 +786,21 @@ VerilogGenerator { value(s GeneratorState, tag Tag, value Value) { s.isNonIndexable = true assert(tag.kind == TagKind.number) + if tag.q == 0 { + write(s, "(((compiler_bug:invalid_num") + } if value.kind == ValueKind.ulong_ { - if tag.q > 0 { - write(s, format("{}'d{}", tag.q, value.z)) - } else { - write(s, format("{}", value.z)) - } + 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) - for i := from - 1; i >= 0; i -= 1 { - Util.writeByteHexTo(data[i], s.out.sb) + if from > 0 { + for i := from - 1; i >= 0; i -= 1 { + Util.writeByteHexTo(data[i], s.out.sb) + } + } else { + write(s, "0") } } else { abandon()