WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.7% of users enabled 2FA.

Commit 4a47a86d authored by Kunshan Wang's avatar Kunshan Wang
Browse files

Parser passes test.

parent 159d0562
......@@ -134,7 +134,7 @@ instBody
| 'EXTRACTVALUE' '<' type intLiteral '>' opnd=value # InstExtractValue
| 'INSERTVALUE' '<' type intLiteral '>' opnd=value newVal=value # InstInsertValue
| 'EXTRACTELEMENT' '<' vecTy=type indTy=type '>' opnd=value index=value # InstExtractElement
| 'INSERTELEMENT' '<' vecTy=type indTy=type '>' opnd=value index=value opnd=value newVal=value # InstInsertElement
| 'INSERTELEMENT' '<' vecTy=type indTy=type '>' opnd=value index=value newVal=value # InstInsertElement
| 'SHUFFLEVECTOR' '<' vecTy=type maskTy=type '>' vec1=value vec2=value mask=value # InstShuffleVector
// Memory Operations
......@@ -165,7 +165,7 @@ instBody
dis=bbName ena=bbName ('WPEXC' '(' wpExc=bbName ')')? keepAliveClause # InstWatchPoint
// Foreign Function Interface
| 'CCALL' '<' funcTy=type funcSig '>' callconv funcCallBody # InstCCall
| 'CCALL' callconv '<' funcTy=type funcSig '>' callee=value argList # InstCCall
// Thread and Stack Operations
| 'NEWSTACK' funcCallBody excClause # InstNewStack
......
......@@ -8,8 +8,8 @@ import uvm.ir.textinput.gen.UIRParser._
import uvm.ir.textinput.gen._
import uvm.ssavariables._
import uvm.types._
import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer
class UIRTextReader(val idFactory: IDFactory) {
import uvm.ir.textinput.Later.Laterable
......@@ -24,12 +24,31 @@ class UIRTextReader(val idFactory: IDFactory) {
read(input, globalBundle)
}
class AccumulativeAntlrErrorListener extends BaseErrorListener {
val buf = new ArrayBuffer[String]()
var hasError = false
override def syntaxError(recognizer: Recognizer[_, _], offendingSymbol: Object,
line: Int, charPositionInLine: Int, msg: String, e: RecognitionException): Unit = {
buf.add("line %d:%d %s".format(line, charPositionInLine, msg))
hasError = true
}
def getMessages(): String = buf.mkString("\n")
}
def read(ir: ANTLRInputStream, globalBundle: Bundle): Bundle = {
val lexer = new UIRLexer(ir)
val tokens = new CommonTokenStream(lexer)
val parser = new UIRParser(tokens)
val ea = new AccumulativeAntlrErrorListener()
parser.removeErrorListeners()
parser.addErrorListener(ea)
val ast = parser.ir()
if (ea.hasError) {
throw new TextIRParsingException("Syntax error:\n"+ea.getMessages)
}
read(ast, globalBundle)
}
implicit def terminalToString(tn: TerminalNode): String = tn.getText()
......@@ -50,7 +69,7 @@ class UIRTextReader(val idFactory: IDFactory) {
}
val abs = prefix match {
case "0x" => BigInt(nums, 16)
case "0" => BigInt(nums, 8)
case "0" => if(nums=="") BigInt(0) else BigInt(nums, 8)
case "" => BigInt(nums, 10)
}
return if (neg) -abs else abs
......@@ -80,7 +99,7 @@ class UIRTextReader(val idFactory: IDFactory) {
case bits: DoubleBitsContext => java.lang.Double.longBitsToDouble(bits.intLiteral().longValue())
}
def cascadeLookup[T <: Identified](name:String, ns1: Namespace[T], ns2: Namespace[T]): T =
def cascadeLookup[T <: Identified](name: String, ns1: Namespace[T], ns2: Namespace[T]): T =
ns1.get(name).getOrElse(ns2(name))
// Printing context information (line, column, near some token)
......@@ -95,18 +114,24 @@ class UIRTextReader(val idFactory: IDFactory) {
return "At %d:%d near '%s': %s".format(line, column, near, s)
}
def catchIn[T](ctx: ParserRuleContext, s: String)(func: => T): T = try {
func
} catch {
case e: Exception => throw new TextIRParsingException(inCtx(ctx, s), e)
}
def read(ir: IrContext, globalBundle: Bundle): Bundle = {
val bundle = new Bundle()
// Resolve global entities. (If any resXxxx is not present, that's because it is simply not currently used)
implicit def resTy(ctx: TypeContext): Type = resTyByName(ctx.getText)
implicit def resTy(ctx: TypeContext): Type = catchIn(ctx, "Unable to resolve type") { resTyByName(ctx.getText) }
def resTyByName(name: String): Type = cascadeLookup(name, bundle.typeNs, globalBundle.typeNs)
implicit def resSig(ctx: FuncSigContext): FuncSig = resSigByName(ctx.getText)
implicit def resSig(ctx: FuncSigContext): FuncSig = catchIn(ctx, "Unable to resolve sig") { resSigByName(ctx.getText) }
def resSigByName(name: String): FuncSig = cascadeLookup(name, bundle.funcSigNs, globalBundle.funcSigNs)
implicit def resConst(ctx: ConstantContext): Constant = resConstByName(ctx.getText)
implicit def resConst(ctx: ConstantContext): Constant = catchIn(ctx, "Unable to resolve const") { resConstByName(ctx.getText) }
def resConstByName(name: String): Constant = cascadeLookup(name, bundle.constantNs, globalBundle.constantNs)
def resGlobalVar(name: String): GlobalVariable = cascadeLookup(name, bundle.globalVarNs, globalBundle.globalVarNs)
......@@ -151,11 +176,11 @@ class UIRTextReader(val idFactory: IDFactory) {
def needArray[T <: Type](tc: TypeContext) = needType(tc, classOf[TypeArray], "array")
def needVector[T <: Type](tc: TypeContext) = needType(tc, classOf[TypeVector], "vector")
def needHybrid[T <: Type](tc: TypeContext) = needType(tc, classOf[TypeHybrid], "hybrid")
def needSeq[T <: Type](tc: TypeContext) = needType(tc, classOf[TypeVector], "array or vector")
def needSeq[T <: Type](tc: TypeContext) = needType(tc, classOf[AbstractSeqType], "array or vector")
// Make types and sigs
val phase1 = new Later() // Resolve inter-references between types and sigs
val phase1 = new Later() // Resolve inter-references between types and sigs
def mkType(tc: TypeConstructorContext): Type = {
val ty = tc match {
......@@ -182,7 +207,7 @@ class UIRTextReader(val idFactory: IDFactory) {
def mkSig(fsc: FuncSigConstructorContext): FuncSig = {
val sig = FuncSig(null, null).later(phase1) { sig =>
sig.retTy = resTy(fsc.retTy)
sig.paramTy = for (t<-fsc.paramTy) yield resTy(t)
sig.paramTy = for (t <- fsc.paramTy) yield resTy(t)
}
return sig
}
......@@ -205,7 +230,7 @@ class UIRTextReader(val idFactory: IDFactory) {
phase1.doAll()
val phase2 = new Later() // Resolve inter-references from constants to other global variables
val phase2 = new Later() // Resolve inter-references from constants to other global variables
def mkConst(t: Type, c: ConstConstructorContext): Constant = {
val con = c match {
......@@ -285,7 +310,7 @@ class UIRTextReader(val idFactory: IDFactory) {
ver.func = func
func.versions = ver :: func.versions
def globalize(name: String): String = if(name(0)=='@') name else (ver.name.get + name.substring(1))
def globalize(name: String): String = if (name(0) == '@') name else (ver.name.get + "." + name.substring(1))
ver.params = fDefCtx.params.name().zipWithIndex.map {
case (n, i) =>
......@@ -296,7 +321,7 @@ class UIRTextReader(val idFactory: IDFactory) {
param
}
val phase4 = new Later() // Resolve references from instructions to other variables (global or local) and basic blocks
val phase4 = new Later() // Resolve references from instructions to other variables (global or local) and basic blocks
def makeBB(bbCtx: BasicBlockContext): BasicBlock = {
val bb = new BasicBlock()
......@@ -311,13 +336,29 @@ class UIRTextReader(val idFactory: IDFactory) {
// Resolve local entities
implicit def resBB(ctx: BbNameContext): BasicBlock = resBBByName(ctx.name.getText)
def resBBByName(name: String): BasicBlock = ver.bbNs(name)
implicit def resBB(ctx: BbNameContext): BasicBlock = catchIn(ctx, "Unable to resolve basic block") {
resBBByName(ctx.name.getText)
}
def resBBByName(name: String): BasicBlock = {
val globalName = globalize(name)
ver.bbNs(globalName)
}
implicit def resVar(ctx: ValueContext): SSAVariable = resVarByName(ctx.name)
def resVarByName(name: String): SSAVariable = cascadeLookup(name, bundle.varNs, globalBundle.varNs)
implicit def resVar(ctx: ValueContext): SSAVariable = catchIn(ctx, "Unable to resolve variable") {
resVarByName(ctx.name)
}
def resVarByName(name: String): SSAVariable = {
val globalName = globalize(name)
cascadeLookup(globalName, bundle.varNs, globalBundle.varNs)
}
def resLocalVar(name: String): LocalVariable = ver.localVarNs(name)
def resLocalVar(ctx: ValueContext): LocalVariable = catchIn(ctx, "Unable to resolve local variable") {
resLocalVarByName(ctx.name)
}
def resLocalVarByName(name: String): LocalVariable = {
val globalName = globalize(name)
ver.localVarNs(globalName)
}
// Resolve special structures
......@@ -325,7 +366,7 @@ class UIRTextReader(val idFactory: IDFactory) {
implicit def resArgList(a: ArgListContext): Seq[SSAVariable] = a.value.map(resVar)
implicit def resKA(ka: KeepAliveClauseContext): Seq[LocalVariable] = ka.value.map(n=>resLocalVar(n.name))
implicit def resKA(ka: KeepAliveClauseContext): Seq[LocalVariable] = ka.value.map(resLocalVar)
implicit def resOrd(ord: MemordContext): MemoryOrder.Value = {
if (ord == null) {
......@@ -384,7 +425,7 @@ class UIRTextReader(val idFactory: IDFactory) {
case ii: InstSwitchContext =>
InstSwitch(ii.`type`, null, null, null).later(phase4) { i =>
i.opnd = ii.opnd; i.defDest = ii.defDest
i.cases = for ((v, b) <- ii.caseVal.zip(ii.caseDest)) yield (resVar(v),resBB(b))
i.cases = for ((v, b) <- ii.caseVal.zip(ii.caseDest)) yield (resVar(v), resBB(b))
}
case ii: InstPhiContext =>
InstPhi(ii.`type`, null).later(phase4) { i =>
......@@ -481,12 +522,12 @@ class UIRTextReader(val idFactory: IDFactory) {
}
case ii: InstCmpXchgContext =>
InstCmpXchg(ii.isWeak != null, ii.ordSucc, ii.ordFail, ii.`type`, null, null, null).later(phase4) { i =>
i.loc = ii.loc; i.expected = ii.expected; i.desired = ii.desired
}
i.loc = ii.loc; i.expected = ii.expected; i.desired = ii.desired
}
case ii: InstAtomicRMWContext =>
InstAtomicRMW(ii.memord, AtomicRMWOptr.withName(ii.atomicrmwop.getText), ii.`type`, null, null).later(phase4) { i =>
i.loc = ii.loc; i.opnd = ii.opnd
}
i.loc = ii.loc; i.opnd = ii.opnd
}
case ii: InstFenceContext =>
InstFence(ii.memord)
case ii: InstTrapContext =>
......@@ -495,11 +536,11 @@ class UIRTextReader(val idFactory: IDFactory) {
}
case ii: InstWatchPointContext =>
InstWatchPoint(ii.intLiteral.intValue(), ii.`type`, null, null, null, null).later(phase4) { i =>
i.dis = ii.dis; i.ena = ii.ena; i.exc = Option(ii.wpExc); i.keepAlives = ii.keepAliveClause
i.dis = ii.dis; i.ena = ii.ena; i.exc = Option(ii.wpExc).map(resBB); i.keepAlives = ii.keepAliveClause
}
case ii: InstCCallContext =>
InstCCall(CallConv.withName(ii.callconv.getText), ii.funcTy, null, null, null).later(phase4) { i =>
asgnFuncCallBody(i, ii.funcCallBody)
i.callee = ii.callee; i.argList = ii.argList
}
case ii: InstNewStackContext =>
InstNewStack(null, null, null, null).later(phase4) { i =>
......@@ -529,7 +570,9 @@ class UIRTextReader(val idFactory: IDFactory) {
}
inst.id = idFactory.getID()
inst.name = Option(instDef.name).map(_.getText)
inst.name = Option(instDef.name).map(n=>globalize(n.getText))
addLocalVar(inst, ver.localVarNs)
return inst
}
......
......@@ -31,7 +31,7 @@ trait ExtraMatchers extends Assertions with Matchers {
def shouldBeAConstFloatOf(something: Any) {
thing shouldBeA[ConstFloat] { its =>
its.ty shouldBeA[TypeFloat] thatsIt
its.constTy shouldBeA[TypeFloat] thatsIt
something match {
case `nan` => assert(its.num.isNaN)
case ExactFloat(num) => its.num shouldEqual num
......@@ -43,7 +43,7 @@ trait ExtraMatchers extends Assertions with Matchers {
def shouldBeAConstDoubleOf(something: Any) {
thing shouldBeA[ConstDouble] { its =>
its.ty shouldBeA[TypeDouble] thatsIt
its.constTy shouldBeA[TypeDouble] thatsIt
something match {
case `nan` => assert(its.num.isNaN)
case ExactDouble(num) => its.num shouldEqual num
......
......@@ -5,8 +5,8 @@ import org.scalatest.Matchers
import uvm.Bundle
class AntlrUvmIRReaderSpec extends AbstractReaderSpec {
override def theSubject = "AntlrUvmIRReader"
class UIRTextReaderSpec extends AbstractReaderSpec {
override def theSubject = "UIRTextReader"
override def parseFile(fileName: String, globalBundle: Bundle): Bundle = {
val idf = new IDFactory()
......
......@@ -31,14 +31,16 @@
.const @cdpinf <@double> = +infd
.const @cdbits <@double> = bitsd(0xfedcba9876543210)
.typedef @s1 = struct<@i64 @cdouble>
.typedef @s1 = struct<@i64 @double>
.const @cs1 <@s1> = {@ci64 @cd}
.typedef @s2 = struct<@float @i64>
.const @cs2 <@s2> = {@cf @ci64}
.typedef @s3 = struct<@double @s2 @i32>
.const @cs2 <@s3> = {@cd @cs2 @ci32}
.const @cs3 <@s3> = {@cd @cs2 @ci32}
.typedef @void = void
.typedef @rv = ref<@void>
.typedef @irv = iref<@void>
......@@ -48,14 +50,14 @@
.typedef @thread = thread
.typedef @stack = stack
.const @cr <@rv> = NULL
.const @cir <@irv> = NULL
.const @cfu <@cf> = NULL
.const @cth <@cth> = NULL
.const @cst <@cst> = NULL
.const @cr <@rv> = NULL
.const @cir <@irv> = NULL
.const @cfu <@func0> = NULL
.const @cth <@thread> = NULL
.const @cst <@stack> = NULL
.typedef @4xfloat = vector <@float 4>
.typedef @4xi32 = vector <@int 4>
.typedef @4xi32 = vector <@i32 4>
.typedef @2xdouble = vector <@double 2>
.const @F_1 <@float> = 1.0f
......@@ -85,6 +87,6 @@
.funcdecl @fdummy <@sig0>
.typedef @ii64 = iref<@i64>
.typedef @sgf_t = struct<@ii64 @sig0>
.typedef @sgf_t = struct<@ii64 @func0>
.const @sgf <@sgf_t> = {@gi64 @fdummy}
......@@ -17,7 +17,8 @@
.typedef @sig_t = func<@sig_fs>
.funcdecl @signal <@sig_t (@i32 @sig_t)>
.funcsig @signal_sig = @sig_t (@i32 @sig_t)
.funcdecl @signal <@signal_sig>
.const @zero <@i32> = 0
......
......@@ -32,7 +32,7 @@
.typedef @tagref64 = tagref64
.typedef @4xfloat = vector <@float 4>
.typedef @4xi32 = vector <@int 4>
.typedef @4xi32 = vector <@i32 4>
.typedef @2xdouble = vector <@double 2>
.const @I8_0 <@i8> = 0
......@@ -154,7 +154,7 @@
RETVOID
}
.funcsig @refCastTest_sig @void (@rv @irv @npnr_func)
.funcsig @refCastTest_sig = @void (@rv @irv @npnr_func)
.funcdef @refCastTest VERSION @refCastTest_v1 <@refCastTest_sig> (%p0 %p1 %p2) {
%entry:
%refcast = REFCAST <@rv @ri32> %p0
......@@ -193,13 +193,13 @@
.funcdecl @callee1 <@npnr_sig>
.funcdef @callee2 VERSION @callee2_v1 <@iiisig> (%p0 %p1) {
.funcdef @callee2 VERSION @callee2_v1 <@iii_sig> (%p0 %p1) {
%entry:
%rv = ADD <@i64> %p0 %p1
%ret = RET <@i64> %rv
}
.funcdef @callee3 VERSION @callee3_v1 <@iiisig> (%p0 %p1) {
.funcdef @callee3 VERSION @callee3_v1 <@iii_sig> (%p0 %p1) {
%entry:
%exc = NEW <@double>
%throw = THROW %exc
......@@ -208,12 +208,12 @@
.funcdef @caller1 VERSION @caller1_v1 <@npnr_sig> () {
%entry:
%v1 = CALL <@npnr_sig> @callee1 ()
%v2 = CALL <@iiisig> @callee2 (@I64_1 @I64_2)
%v3 = CALL <@iiisig> @callee3 (@I64_1 @I64_2) EXC(%cont %catch)
%v2 = CALL <@iii_sig> @callee2 (@I64_1 @I64_2)
%v3 = CALL <@iii_sig> @callee3 (@I64_1 @I64_2) EXC(%cont %catch)
%cont:
%v4 = CALL <@npnr_sig> @callee1 () KEEPALIVE(%v2 %v3)
%v5 = CALL <@iiisig> @callee3 (%v3 %v3) EXC(%cont2 %catch) KEEPALIVE(%v2)
%v5 = CALL <@iii_sig> @callee3 (%v3 %v3) EXC(%cont2 %catch) KEEPALIVE(%v2)
%cont2:
%retv = RETVOID
......@@ -223,9 +223,9 @@
RETVOID
}
.funcdef @caller2 VERSION @caller2_v1 <@iiisig> (%p0 %p1) {
.funcdef @caller2 VERSION @caller2_v1 <@iii_sig> (%p0 %p1) {
%entry:
%tc = TAILCALL <@iiisig> @callee2 (%p0 %p1)
%tc = TAILCALL <@iii_sig> @callee2 (%p0 %p1)
}
.typedef @sid = struct <@i64 @double>
......@@ -254,21 +254,21 @@
.typedef @al = array <@i64 10>
.typedef @hic = hybrid <@i64 @i8>
.funcsig @memops_sig = @void <@i64 @i64>
.funcsig @memops_sig = @void (@i64 @i64)
.funcdef @memops VERSION @memops_v1 <@memops_sig> (%p0 %p1) {
%entry:
%new = NEW <@i64>
%newhybrid = NEWHYBRID <@hic> %p0
%newhybrid = NEWHYBRID <@hic @i64> %p0
%alloca = ALLOCA <@i64 >
%allocahybrid = ALLOCAHYBRID <@hic> %p0
%allocahybrid = ALLOCAHYBRID <@hic @i64> %p0
%new_s = NEW <@i64> EXC(%bb2 %handler)
%bb2:
%newhybrid_s = NEWHYBRID <@hic> %p0 EXC(%bb3 %handler)
%newhybrid_s = NEWHYBRID <@hic @i64> %p0 EXC(%bb3 %handler)
%bb3:
%alloca_s = ALLOCA <@i64 > EXC(%bb4 %handler)
%bb4:
%allocahybrid_s = ALLOCAHYBRID <@hic> %p0 EXC(%bb5 %handler)
%allocahybrid_s = ALLOCAHYBRID <@hic @i64> %p0 EXC(%bb5 %handler)
%bb5:
%new2 = NEW <@sid>
......@@ -291,14 +291,14 @@
%atomicrmw = ATOMICRMW SEQ_CST ADD <@i64> %alloca @I64_43
%load_s = LOAD <@i64> %alloca EXC(%bb6 %handler)
%bb6
%bb6:
%store_s = STORE <@i64> %alloca @I64_42 EXC(%bb7 %handler)
%bb7
%bb7:
%cmpxchg_s = CMPXCHG SEQ_CST SEQ_CST <@i64> %alloca @I64_42 @I64_0 EXC(%bb8 %handler)
%bb8
%bb8:
%atomicrmw_s= ATOMICRMW SEQ_CST ADD <@i64> %alloca @I64_43 EXC(%bb9 %handler)
%bb9
%bb9:
%fence = FENCE SEQ_CST
RETVOID
......@@ -316,7 +316,7 @@
%l2 = LOAD CONSUME <@i64> %p0
%l3 = LOAD ACQUIRE <@i64> %p0
%s4 = STORE RELEASE <@i64> %p0 @I64_42
%c5 = CMPXCHG ACQ_REL <@i64> %p0 @I64_42 @I64_43
%c5 = CMPXCHG ACQ_REL ACQUIRE <@i64> %p0 @I64_42 @I64_43
%l6 = LOAD SEQ_CST <@i64> %p0
RETVOID
......@@ -378,7 +378,7 @@
.funcsig @ccall_sig = @void (@i64)
.funcdef @ccall VERSION @ccall_v1 <@ccall_sig> (%p0) {
%entry:
%rv = CCALL DEFAULT <@ccall_callee_sig @i64> %p0 (@D_1)
%rv = CCALL DEFAULT <@i64 @ccall_callee_sig> %p0 (@D_1)
RETVOID
}
......@@ -386,17 +386,17 @@
.funcsig @gen_sig = @void (@stack)
.funcdef @gen VERSION @gen_v1 <@npnr_sig> (%main) {
%entry:
%ss1 = SWAPSTACK %main RET_WITH<@void> PASS_VALUE(@i64_0)
%ss2 = SWAPSTACK %main KILL_OLD THROW_EXC(@NULLREF)
%ss1 = SWAPSTACK %main RET_WITH <@void> PASS_VALUE <@i64> @I64_0
%ss2 = SWAPSTACK %main KILL_OLD THROW_EXC @NULLREF
THROW @NULLREF // unreachable
}
.funcdef @swapstack VERSION @swapstack_v1 <@npnr_sig> () {
%entry:
%curstack = COMMINST @uvm.cur_stack
%coro = NEWSTACK <@iiisig> @callee2 (%curstack)
%ss1 = SWAPSTACK %coro RET_WITH<@i64> PASS_VOID
%ss2 = SWAPSTACK %coro RET_WITH<@i64> PASS_VOID EXC(%nor %exc)
%curstack = COMMINST @uvm.current_stack
%coro = NEWSTACK <@iii_sig> @callee2 (%curstack)
%ss1 = SWAPSTACK %coro RET_WITH <@i64> PASS_VOID
%ss2 = SWAPSTACK %coro RET_WITH <@i64> PASS_VOID EXC(%nor %exc)
%nor:
RETVOID
......@@ -406,9 +406,9 @@
.funcdef @comminst VERSION @comminst_v1 <@npnr_sig> () {
%entry:
%curstack = COMMINST @uvm.cur_stack
%sta = NEWSTACK <@iiisig> @callee2 (%curstack)
%thr = COMMINST @new_stack (%sta)
%th_ex = COMMINST @thread_exit
%curstack = COMMINST @uvm.current_stack
%sta = NEWSTACK <@iii_sig> @callee2 (%curstack)
%thr = COMMINST @uvm.new_thread (%sta)
%th_ex = COMMINST @uvm.thread_exit
RETVOID
}
......@@ -43,5 +43,5 @@
.typedef @tr64 = tagref64
.typedef @4xfloat = vector <@float 4>
.typedef @4xi32 = vector <@int 4>
.typedef @4xi32 = vector <@i32 4>
.typedef @2xdouble = vector <@double 2>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment