blob: 368dffead1b36e6e19376a259775458a65bd7e26 [file] [edit]
// Copyright (c) 2026, 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 'package:cfg/front_end/recognized_methods.dart';
import 'package:cfg/ir/constant_value.dart';
import 'package:cfg/ir/field.dart';
import 'package:cfg/ir/flow_graph_builder.dart';
import 'package:cfg/ir/functions.dart';
import 'package:cfg/ir/global_context.dart';
import 'package:cfg/ir/instructions.dart';
import 'package:cfg/ir/types.dart';
import 'package:kernel/ast.dart' as ast;
import 'package:kernel/external_name.dart' show getExternalName;
import 'package:native_compiler/runtime/constant_objects.dart';
import 'package:native_compiler/runtime/object_layout.dart';
import 'package:native_compiler/runtime/vm_defs.dart';
import 'package:vm/modular/transformations/pragma.dart';
/// Build IR for native methods.
void buildNativeMethod(
FlowGraphBuilder builder,
FunctionRegistry functionRegistry,
CFunction function,
) {
// Reserve extra argument slot for return value.
builder.addNullConstant();
final functionNode = function.functionNode!;
final argsShape = functionRegistry.getArgumentsShape(
(function.hasReceiverParameter ? 1 : 0) +
functionNode.positionalParameters.length +
1,
types: function.numberOfFunctionTypeParameters,
named: functionNode.namedParameters.map((p) => p.parameterName).toList(),
);
builder.addExternalCall(
function,
function.numberOfParameters + 1,
argsShape,
function.returnType,
);
builder.addReturn();
}
/// Build IR for static getters returning constants.
void buildConstantGetter(FlowGraphBuilder builder, ConstantValue value) {
builder.addConstant(value);
builder.addReturn();
}
/// Build IR for instance field getters.
void buildInstanceGetter(FlowGraphBuilder builder, CField field) {
builder.addLoadInstanceField(field, checkInitialized: field.isLate);
builder.addReturn();
}
/// Build IR for instance field setters.
void buildInstanceSetter(FlowGraphBuilder builder, CField field) {
builder.addStoreInstanceField(
field,
checkNotInitialized: field.isLate && field.isFinal,
);
builder.addNullConstant();
builder.addReturn();
}
/// Build IR for unary int operations
void buildUnaryIntOp(FlowGraphBuilder builder, UnaryIntOpcode op) {
builder.addUnaryIntOp(op);
builder.addReturn();
}
/// Build IR for indexed load of an array element.
void buildArrayElementGetter(
FlowGraphBuilder builder,
ArrayKind kind,
CField lengthField,
CType elemType,
) {
final index = builder.pop();
final array = builder.pop();
builder.push(array);
builder.push(index);
builder.push(array);
builder.addLoadInstanceField(lengthField);
builder.addIndexCheck();
builder.addLoadArrayElement(kind, elemType);
builder.addReturn();
}
/// Build IR for unimplemented methods marked with 'vm:recognized' pragma.
void buildUnimplementedRecognizedMethod(
FlowGraphBuilder builder,
CFunction function,
) {
// Drop parameters.
for (var i = 0, n = function.numberOfParameters; i < n; ++i) {
builder.pop();
}
builder.addConstant(
ConstantValue.fromString('Unimplemented recognized method: $function'),
);
builder.addThrow(.exception, 1);
}
extension on ArrayKind {
String get className => switch (this) {
.int8List => '_Int8List',
.uint8List => '_Uint8List',
.uint8ClampedList => '_Uint8ClampedList',
.int16List => '_Int16List',
.uint16List => '_Uint16List',
.int32List => '_Int32List',
.uint32List => '_Uint32List',
.int64List => '_Int64List',
.uint64List => '_Uint64List',
};
}
/// VM-specific recognized methods.
final class VmRecognizedMethods(
final FunctionRegistry functionRegistry,
final ObjectLayout objectLayout,
) extends CommonRecognizedMethods {
final coreTypes = GlobalContext.instance.coreTypes;
@override
BuildIR? getRecognizedFunctionBody(CFunction function) {
final member = function.member;
if (member.isRecognized(coreTypes)) {
final builder = _recognizedMembers[member];
if (builder != null) {
return builder;
}
if (member.isExternal) {
return (FlowGraphBuilder builder) {
buildUnimplementedRecognizedMethod(builder, function);
};
}
}
if (member.isExternal) {
if (getExternalName(coreTypes, member) != null) {
return (FlowGraphBuilder builder) {
buildNativeMethod(builder, functionRegistry, function);
};
}
}
return super.getRecognizedFunctionBody(function);
}
late final _recognizedMembers = <ast.Member, BuildIR>{
// dart:core
index.getProcedure(
'dart:core',
'_Smi',
'get:hashCode',
): (FlowGraphBuilder builder) {
buildUnaryIntOp(builder, .hash);
},
index.getProcedure(
'dart:core',
'_Smi',
'get:bitLength',
): (FlowGraphBuilder builder) {
buildUnaryIntOp(builder, .bitLength);
},
index.getProcedure(
'dart:core',
'_Mint',
'get:hashCode',
): (FlowGraphBuilder builder) {
buildUnaryIntOp(builder, .hash);
},
index.getProcedure(
'dart:core',
'_Mint',
'get:bitLength',
): (FlowGraphBuilder builder) {
buildUnaryIntOp(builder, .bitLength);
},
index.getProcedure(
'dart:core',
'_Array',
'get:length',
): (FlowGraphBuilder builder) {
buildInstanceGetter(builder, objectLayout.Array_length);
},
// dart:_compact_hash
index.getTopLevelProcedure(
'dart:_compact_hash',
'get:_uninitializedIndex',
): (FlowGraphBuilder builder) {
buildConstantGetter(
builder,
ConstantValue(RuntimeConstantObject(.uninitializedIndex)),
);
},
index.getTopLevelProcedure(
'dart:_compact_hash',
'get:_uninitializedData',
): (FlowGraphBuilder builder) {
buildConstantGetter(
builder,
ConstantValue(RuntimeConstantObject(.uninitializedData)),
);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'get:_index',
): (FlowGraphBuilder builder) {
buildInstanceGetter(builder, objectLayout.LinkedHashBase_index);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'set:_index',
): (FlowGraphBuilder builder) {
buildInstanceSetter(builder, objectLayout.LinkedHashBase_index);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'get:_hashMask',
): (FlowGraphBuilder builder) {
buildInstanceGetter(builder, objectLayout.LinkedHashBase_hashMask);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'set:_hashMask',
): (FlowGraphBuilder builder) {
buildInstanceSetter(builder, objectLayout.LinkedHashBase_hashMask);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'get:_data',
): (FlowGraphBuilder builder) {
buildInstanceGetter(builder, objectLayout.LinkedHashBase_data);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'set:_data',
): (FlowGraphBuilder builder) {
buildInstanceSetter(builder, objectLayout.LinkedHashBase_data);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'get:_usedData',
): (FlowGraphBuilder builder) {
buildInstanceGetter(builder, objectLayout.LinkedHashBase_usedData);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'set:_usedData',
): (FlowGraphBuilder builder) {
buildInstanceSetter(builder, objectLayout.LinkedHashBase_usedData);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'get:_deletedKeys',
): (FlowGraphBuilder builder) {
buildInstanceGetter(builder, objectLayout.LinkedHashBase_deletedKeys);
},
index.getProcedure(
'dart:_compact_hash',
'_LinkedHashBase',
'set:_deletedKeys',
): (FlowGraphBuilder builder) {
buildInstanceSetter(builder, objectLayout.LinkedHashBase_deletedKeys);
},
// dart:_internal
index.getTopLevelProcedure(
'dart:_internal',
'get:has63BitSmis',
): (FlowGraphBuilder builder) {
final totalSmiBits =
smiBits(objectLayout.compressedWordSize) + 1; // Including sign bit.
final has63BitSmis = totalSmiBits >= 63;
buildConstantGetter(builder, ConstantValue.fromBool(has63BitSmis));
},
// dart:typed_data
index.getProcedure(
'dart:typed_data',
'_TypedListBase',
'get:length',
): (FlowGraphBuilder builder) {
buildInstanceGetter(builder, objectLayout.TypedListBase_length);
},
for (ArrayKind arrayKind in [
.int8List,
.uint8List,
.uint8ClampedList,
.int16List,
.uint16List,
.int32List,
.uint32List,
.int64List,
.uint64List,
])
index.getProcedure(
'dart:typed_data',
arrayKind.className,
'[]',
): (FlowGraphBuilder builder) {
buildArrayElementGetter(
builder,
arrayKind,
objectLayout.TypedListBase_length,
const IntType(),
);
},
};
}