| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:convert'; |
| |
| import 'package:analyzer_utilities/package_root.dart' as pkg_root; |
| import 'package:analyzer_utilities/tools.dart'; |
| import 'package:collection/collection.dart'; |
| import 'package:path/path.dart'; |
| |
| Future<void> main() async { |
| await GeneratedContent.generateAll(analyzerPkgPath, allTargets); |
| } |
| |
| final allTargets = [ |
| GeneratedFile( |
| 'lib/src/wolf/ir/ir.g.dart', (pkgPath) async => _IrGenerator().run()) |
| ]; |
| |
| final analyzerPkgPath = normalize(join(pkg_root.packageRoot, 'analyzer')); |
| |
| final _instructions = _Instructions(); |
| |
| sealed class _Encoding { |
| final String type; |
| |
| const _Encoding(this.type); |
| |
| @override |
| int get hashCode => type.hashCode; |
| |
| @override |
| operator ==(other) => other is _Encoding && type == other.type; |
| |
| _Parameter call(String name) => _Parameter._(name, this); |
| |
| String decode(String value); |
| |
| String encode(String value); |
| |
| String stringInterpolation(String value); |
| } |
| |
| class _Instruction { |
| final String name; |
| final List<_Parameter> parameters; |
| final int parameterShapeId; |
| |
| _Instruction(this.name, this.parameters, this.parameterShapeId); |
| |
| String get className => '_${name.capitalized}Instruction'; |
| |
| String get signature { |
| var parametersString = |
| [for (var p in parameters) '${p.encoding.type} ${p.name}'].join(', '); |
| return '$name($parametersString)'; |
| } |
| } |
| |
| class _Instructions { |
| late final sorted = all.toList()..sort((a, b) => a.name.compareTo(b.name)); |
| |
| final all = <_Instruction>[]; |
| |
| final encodings = <_NontrivialEncoding>[]; |
| |
| final parameterShapeMap = <_ParameterShape, int>{}; |
| |
| _Instructions() { |
| // Encodings |
| var callDescriptor = encoding('CallDescriptorRef'); |
| var argumentNames = encoding('ArgumentNamesRef'); |
| var functionFlags = |
| encoding('FunctionFlags', fieldName: '_flags', constructorName: '_'); |
| var literal = encoding('LiteralRef'); |
| var stackIndices = encoding('StackIndicesRef'); |
| var type = encoding('TypeRef'); |
| const uint = _TrivialEncoding('int'); |
| |
| // Local variable access |
| _addInstruction('alloc', [uint('count')]); |
| _addInstruction('release', [uint('count')]); |
| _addInstruction('readLocal', [uint('localIndex')]); |
| _addInstruction('writeLocal', [uint('localIndex')]); |
| // Primitive operations |
| _addInstruction('literal', [literal('value')]); |
| _addInstruction('identical', []); |
| _addInstruction('eq', []); |
| _addInstruction('not', []); |
| _addInstruction('concat', [uint('count')]); |
| _addInstruction('is_', [type('type')]); |
| // Stack manipulation |
| _addInstruction('drop', []); |
| _addInstruction('dup', []); |
| _addInstruction( |
| 'shuffle', [uint('popCount'), stackIndices('stackIndices')]); |
| // Flow control |
| _addInstruction('block', [uint('inputCount'), uint('outputCount')]); |
| _addInstruction('loop', [uint('inputCount')]); |
| _addInstruction('function', [type('type'), functionFlags('flags')]); |
| _addInstruction('end', []); |
| _addInstruction('br', [uint('nesting')]); |
| _addInstruction('brIf', [uint('nesting')]); |
| _addInstruction('await_', []); |
| _addInstruction('yield_', []); |
| // Invocations and tearoffs |
| _addInstruction('call', |
| [(callDescriptor('callDescriptor')), (argumentNames('argumentNames'))]); |
| } |
| |
| _NontrivialEncoding encoding(String type, |
| {String fieldName = 'index', String constructorName = ''}) { |
| var encoding = _NontrivialEncoding(type, |
| fieldName: fieldName, constructorName: constructorName); |
| encodings.add(encoding); |
| return encoding; |
| } |
| |
| void _addInstruction(String name, List<_Parameter> parameters) { |
| var parameterShapeId = parameterShapeMap.putIfAbsent( |
| _ParameterShape(parameters), () => parameterShapeMap.length); |
| all.add(_Instruction(name, parameters, parameterShapeId)); |
| } |
| } |
| |
| class _IrGenerator { |
| final _substringsToOutput = <String>[]; |
| |
| void blankLine() { |
| output('\n'); |
| } |
| |
| void output(String s) { |
| _substringsToOutput.add(s); |
| } |
| |
| void outputIRToStringMixin() { |
| output(''' |
| mixin IRToStringMixin implements RawIRContainerInterface { |
| String instructionToString(int address) { |
| switch (opcodeAt(address)) { |
| '''); |
| _instructions.all.forEachSeparated(blankLine, (instruction) { |
| var opcode = 'Opcode.${instruction.name}'; |
| var interpolation = instruction.name.demangled; |
| if (instruction.parameters.isNotEmpty) { |
| var interpolationParts = <String>[]; |
| for (var p in instruction.parameters) { |
| interpolationParts.add(p.encoding.stringInterpolation( |
| '$opcode.decode${p.name.capitalized}(this, address)')); |
| } |
| interpolation += '(${interpolationParts.join(', ')})'; |
| } |
| output(''' |
| case $opcode: |
| return '$interpolation'; |
| '''); |
| }); |
| output(''' |
| default: |
| return '???'; |
| } |
| } |
| } |
| |
| '''); |
| } |
| |
| void outputOpcode() { |
| output(''' |
| // TODO(paulberry): when extension types are supported, make this an extension |
| // type, as well as all the `_ParameterShape` classes. |
| class Opcode { |
| final int index; |
| |
| const Opcode._(this.index); |
| |
| '''); |
| _instructions.all.forEachIndexed((i, instruction) { |
| var shapeId = instruction.parameterShapeId; |
| output(' static const ${instruction.name} = ' |
| '_ParameterShape$shapeId._(${i++});\n'); |
| }); |
| output(''' |
| |
| String describe() => opcodeNameTable[index]; |
| |
| static const opcodeNameTable = [ |
| '''); |
| _instructions.all.forEachIndexed((i, instruction) { |
| output(' ${json.encode(instruction.name.demangled)},'); |
| }); |
| output(''' |
| ]; |
| } |
| |
| '''); |
| } |
| |
| void outputParameterShapes() { |
| _instructions.parameterShapeMap.forEach((parameterShape, id) { |
| output(''' |
| class _ParameterShape$id extends Opcode { |
| const _ParameterShape$id._(super.index) : super._(); |
| '''); |
| var i = 0; |
| for (var parameter in parameterShape._parameters) { |
| var returnType = parameter.encoding.type; |
| var name = 'decode${parameter.name.capitalized}'; |
| var value = parameter.encoding.decode('ir._params${i++}[address]'); |
| output(''' |
| |
| $returnType $name(RawIRContainerInterface ir, int address) { |
| assert(ir.opcodeAt(address).index == index); |
| return $value; |
| } |
| '''); |
| } |
| output(''' |
| } |
| |
| '''); |
| }); |
| } |
| |
| void outputRawIRWriterMixin() { |
| output(''' |
| mixin _RawIRWriterMixin implements _RawIRWriterMixinInterface { |
| '''); |
| _instructions.sorted.forEachSeparated(blankLine, (instruction) { |
| output(''' |
| void ${instruction.signature} { |
| _opcodes.add(Opcode.${instruction.name}); |
| '''); |
| var i = 0; |
| for (var p in instruction.parameters) { |
| output(' _params${i++}.add(${p.encoding.encode(p.name)});\n'); |
| } |
| while (i < 2) { |
| output(' _params${i++}.add(0);\n'); |
| } |
| output(''' |
| } |
| '''); |
| }); |
| output('}\n\n'); |
| } |
| |
| String run() { |
| output(r''' |
| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // THIS FILE IS GENERATED. DO NOT EDIT. |
| // |
| // Instead modify 'pkg/analyzer/tool/wolf/generate.dart' and run |
| // 'dart run pkg/analyzer/tool/wolf/generate.dart' to update. |
| |
| part of 'ir.dart'; |
| |
| '''); |
| outputRawIRWriterMixin(); |
| outputIRToStringMixin(); |
| outputParameterShapes(); |
| outputOpcode(); |
| return _substringsToOutput.join(); |
| } |
| } |
| |
| class _NontrivialEncoding extends _Encoding { |
| final String fieldName; |
| final String constructorName; |
| |
| _NontrivialEncoding(super.type, |
| {required this.fieldName, required this.constructorName}); |
| |
| @override |
| String decode(String value) => |
| '$type${constructorName.isEmpty ? '' : '.$constructorName'}($value)'; |
| |
| @override |
| String encode(String value) => '$value.$fieldName'; |
| |
| @override |
| String stringInterpolation(String value) => |
| '\${${type.uncapitalized}ToString($value)}'; |
| } |
| |
| class _Parameter { |
| final String name; |
| final _Encoding encoding; |
| |
| _Parameter._(this.name, this.encoding); |
| |
| String get fieldName => '_$name'; |
| |
| @override |
| int get hashCode => Object.hash(name, encoding); |
| |
| @override |
| bool operator ==(other) => |
| other is _Parameter && name == other.name && encoding == other.encoding; |
| } |
| |
| class _ParameterShape { |
| final List<_Parameter> _parameters; |
| |
| _ParameterShape(this._parameters); |
| |
| @override |
| int get hashCode => Object.hashAll(_parameters); |
| |
| @override |
| bool operator ==(other) { |
| if (other is! _ParameterShape || |
| _parameters.length != other._parameters.length) { |
| return false; |
| } |
| for (var i = 0; i < _parameters.length; i++) { |
| if (_parameters[i] != other._parameters[i]) return false; |
| } |
| return true; |
| } |
| } |
| |
| class _TrivialEncoding extends _Encoding { |
| const _TrivialEncoding(super.type); |
| |
| @override |
| String decode(String value) => value; |
| |
| @override |
| String encode(String value) => value; |
| |
| @override |
| String stringInterpolation(String value) => '\${$value}'; |
| } |
| |
| extension<T> on List<T> { |
| forEachSeparated(void Function() separator, void Function(T) callback) { |
| void Function()? nextSeparator; |
| for (var item in this) { |
| nextSeparator?.call(); |
| callback(item); |
| nextSeparator = separator; |
| } |
| } |
| } |
| |
| extension on String { |
| String get capitalized => '${this[0].toUpperCase()}${substring(1)}'; |
| |
| String get demangled => endsWith('_') ? substring(0, length - 1) : this; |
| |
| String get uncapitalized => '${this[0].toLowerCase()}${substring(1)}'; |
| } |