#!/usr/bin/env python # Copyright (C) 2016 Apple Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met:n # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This tool has a couple of helpful macros to process Wasm files from the wasm.json. from generateWasm import * import optparse import sys import re parser = optparse.OptionParser(usage="usage: %prog ") (options, args) = parser.parse_args(sys.argv[0:]) if len(args) != 3: parser.error(parser.usage) wasm = Wasm(args[0], args[1]) opcodes = wasm.opcodes wasmB3IRGeneratorHFile = open(args[2], "w") opcodeRegex = re.compile('([a-zA-Z0-9]+)') argumentRegex = re.compile('(\@[0-9]+)') decimalRegex = re.compile('([-]?[0-9]+)') whitespaceRegex = re.compile('\s+') commaRegex = re.compile('(,)') oparenRegex = re.compile('(\()') cparenRegex = re.compile('(\))') class Source: def __init__(self, contents, offset=0): self.contents = contents self.offset = offset def read(regex, source): match = regex.match(source.contents, source.offset) if not match: return None source.offset = match.end() return match.group() def lex(source): result = [] while source.offset != len(source.contents): read(whitespaceRegex, source) opcode = read(opcodeRegex, source) if opcode: result.append(opcode) continue argument = read(argumentRegex, source) if argument: result.append(argument) continue number = read(decimalRegex, source) if number: result.append(int(number)) continue oparen = read(oparenRegex, source) if oparen: result.append(oparen) continue cparen = read(cparenRegex, source) if cparen: result.append(cparen) continue comma = read(commaRegex, source) if comma: # Skip commas continue raise Exception("Lexing Error: could not lex token from: " + source.contents + " at offset: " + str(source.offset) + " (" + source.contents[source.offset:] + "). With tokens: [" + ", ".join(result) + "]") return result class CodeGenerator: def __init__(self, tokens): self.tokens = tokens self.index = 0 self.code = [] def advance(self): self.index += 1 def token(self): return self.tokens[self.index] def parseError(self, string): raise Exception("Parse error " + string) def consume(self, string): if self.token() != string: self.parseError("Expected " + string + " but got " + self.token()) self.advance() def generateParameters(self): self.advance() params = [] tokens = self.tokens while self.index < len(tokens): if self.token() == ")": self.advance() return params params.append(self.generateOpcode()) self.parseError("Parsing arguments fell off end") def generateOpcode(self): result = None if self.token() == "i32" or self.token() == "i64": type = "Int32" if self.token() == "i64": type = "Int64" self.advance() self.consume("(") self.code.append(generateConstCode(self.index, self.token(), type)) result = temp(self.index) self.advance() self.consume(")") elif argumentRegex.match(self.token()): result = "arg" + self.token()[1:] self.advance() else: op = self.token() index = self.index self.advance() params = self.generateParameters() self.code.append(generateB3OpCode(index, op, params)) result = temp(index) return result def generate(self, wasmOp): if len(self.tokens) == 1: params = ["arg" + str(param) for param in range(len(wasmOp["parameter"]))] return " result = m_currentBlock->appendNew(m_proc, B3::" + self.token() + ", origin(), " + ", ".join(params) + ")" result = self.generateOpcode() self.code.append("result = " + result) return " " + " \n".join(self.code) def temp(index): return "temp" + str(index) def generateB3OpCode(index, op, params): return "Value* " + temp(index) + " = m_currentBlock->appendNew(m_proc, B3::" + op + ", origin(), " + ", ".join(params) + ");" def generateConstCode(index, value, type): return "Value* " + temp(index) + " = constant(" + type + ", " + value + ");" def generateB3Code(wasmOp, source): tokens = lex(Source(source)) parser = CodeGenerator(tokens) return parser.generate(wasmOp) def generateSimpleCode(op): opcode = op["opcode"] b3op = opcode["b3op"] args = ["ExpressionType arg" + str(param) for param in range(len(opcode["parameter"]))] args.append("ExpressionType& result") return """ template<> auto B3IRGenerator::addOp(" + ", ".join(args) + """) -> PartialResult { """ + generateB3Code(opcode, b3op) + """; return { }; } """ definitions = [generateSimpleCode(op) for op in wasm.opcodeIterator(lambda op: isSimple(op) and (isBinary(op) or isUnary(op)))] contents = wasm.header + """ #pragma once #if ENABLE(WEBASSEMBLY) namespace JSC { namespace Wasm { """ + "".join(definitions) + """ } } // namespace JSC::Wasm #endif // ENABLE(WEBASSEMBLY) """ wasmB3IRGeneratorHFile.write(contents) wasmB3IRGeneratorHFile.close()