blob: 48b835c7f918133d1dda6386a580ee06dcb84f87 [file] [log] [blame]
// 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)}';
}