import {Source} from ../sources; import {PatioLocalRef, PatioOperand} from ./operands; import {PatioDataLayout} from ./types; sum PatioInstruction { variant PatioBlock; variant PatioBoolToInt; variant PatioCall; variant PatioCallPointer; variant PatioDataPtrToInt; variant PatioIf; variant PatioIntAdd; variant PatioIntEquals; variant PatioIntNotEqual; variant PatioIntSub; variant PatioIntToBool; variant PatioIntToDataPtr; variant PatioLocalStore; variant PatioLoop; variant PatioLoopBreak; variant PatioLoopContinue; variant PatioMemoryLoad; variant PatioMemoryStore; variant PatioReturn; variant PatioSwitch; variant PatioThrow; variant PatioTry; variant PatioUnreachable; variant PatioVarLoad; variant PatioVarStore; } /// Execute the instructions in the body of the block. The results may be used in /// the parent scope, but any other variables (as distinct from values) are no /// longer in use. struct PatioBlock { field body: List[PatioInstruction]; field results: List[PatioOperand]; field source: Source; } struct PatioBoolToInt { field destination: PatioLocalRef; field value: PatioOperand; field source: Source; } /// Call the function with the given name, and store the returned values in the /// destination locals. struct PatioCall { field destinations: List[PatioLocalRef]; field functionName: String; field args: List[PatioOperand]; field source: Source; } /// Call the function pointed to by the function pointer with the given /// arguments, and store the returned values in the destination locals. struct PatioCallPointer { field destinations: List[PatioLocalRef]; field functionPointer: PatioOperand; field args: List[PatioOperand]; field source: Source; } struct PatioDataPtrToInt { field destination: PatioLocalRef; field value: PatioOperand; field source: Source; } /// If the condition is true, execute the instructions in the "true" body, /// otherwise execute the instructions in the "false" body. /// /// Each block must have the same number of results as destinations. Each result is /// assigned to the destination with the same index. struct PatioIf { field destinations: List[PatioLocalRef]; field condition: PatioOperand; field trueBody: PatioBlock; field falseBody: PatioBlock; field source: Source; } struct PatioIntAdd { field destination: PatioLocalRef; field left: PatioOperand; field right: PatioOperand; field source: Source; } struct PatioIntEquals { field destination: PatioLocalRef; field left: PatioOperand; field right: PatioOperand; field source: Source; } /// Compare the left and right operands. If they are equal, store false in the /// destination, otherwise store true in the destination. struct PatioIntNotEqual { field destination: PatioLocalRef; field left: PatioOperand; field right: PatioOperand; field source: Source; } /// Calculate the left operand minus the right operand, and store the result in /// the destination. struct PatioIntSub { field destination: PatioLocalRef; field left: PatioOperand; field right: PatioOperand; field source: Source; } struct PatioIntToBool { field destination: PatioLocalRef; field value: PatioOperand; field source: Source; } struct PatioIntToDataPtr { field destination: PatioLocalRef; field value: PatioOperand; field source: Source; } /// Store the given value in the destination local. struct PatioLocalStore { field destination: PatioLocalRef; field value: PatioOperand; field source: Source; } /// Execute the body. The `continue` instruction can jump back to the /// start of the body. The `break` instruction can jump to the end of /// the body. struct PatioLoop { field body: List[PatioInstruction]; field source: Source; } /// Jump to the end of the enclosing `loop` instruction's body. struct PatioLoopBreak { field source: Source; } /// Jump to the start of the enclosing `loop` instruction's body. struct PatioLoopContinue { field source: Source; } /// Load the value at the given offset from the given pointer and store it in the /// destination local. struct PatioMemoryLoad { field destination: PatioLocalRef; field pointer: PatioOperand; field layout: PatioDataLayout; field elementOffsets: List[Int32]; field source: Source; } /// Store the given value at the given offset from the given pointer. struct PatioMemoryStore { field pointer: PatioOperand; field layout: PatioDataLayout; field elementOffsets: List[Int32]; field value: PatioOperand; field source: Source; } /// Return with the given values from the current function. struct PatioReturn { field values: List[PatioOperand]; field source: Source; } /// Find a case with a value matching that of the instruction's value. If such a /// case is found, execute the body of that case, otherwise execute the default /// body. /// /// Each block must have the same number of results as destinations. Each result /// is assigned to the destination with the same index. struct PatioSwitch { field destinations: List[PatioLocalRef]; field value: PatioOperand; field cases: List[PatioSwitchCase]; field defaultBody: PatioBlock; field source: Source; } struct PatioSwitchCase { field value: Int32; field body: PatioBlock; field source: Source; } /// Search the stack for an exception handler with a catch clause matching the /// thrown exception type. Unwind the stack up to and including this exception /// handler, and run the body of the catch clause. If no such exception handler /// is found, the process should abort. struct PatioThrow { field exceptionTypeName: String; field value: PatioOperand; field source: Source; } /// Push an exception handler with the given catch clauses onto the control flow /// stack, and then execute the instructions in its body. /// /// If the exception handler catches an exception, the body of the corresponding /// is executed instead. /// /// Each block (that is, the body of the try and the body of each catch clause) /// must have the same number of results as destinations. Each result is assigned to /// the destination with the same index. struct PatioTry { field destinations: List[PatioLocalRef]; field catchClauses: List[PatioCatchClause]; field body: PatioBlock; field source: Source; } /// A catch clause can accept an exception of a given type. If executed, it will /// stored the value of the exception in the given destination, and then execute the /// body. struct PatioCatchClause { field exceptionTypeName: String; field destination: PatioLocalRef; field body: PatioBlock; field source: Source; } /// Unreachable instructions have undefined behaviour, and are used to indicate /// that an instruction sequence is unreachable. struct PatioUnreachable { field source: Source; } /// Load the value from the given var and store it in the destination local. struct PatioVarLoad { field destination: PatioLocalRef; field var: PatioLocalRef; field source: Source; } /// Store the given value in the destination var. struct PatioVarStore { field var: PatioLocalRef; field value: PatioOperand; field source: Source; }