mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
Merge branch 'wasm-gc-linear-memory' into eagler-r1
This commit is contained in:
commit
0d4da6e99a
|
@ -33,6 +33,7 @@ import org.teavm.backend.wasm.gc.TeaVMWasmGCHost;
|
||||||
import org.teavm.backend.wasm.gc.WasmGCClassConsumer;
|
import org.teavm.backend.wasm.gc.WasmGCClassConsumer;
|
||||||
import org.teavm.backend.wasm.gc.WasmGCClassConsumerContext;
|
import org.teavm.backend.wasm.gc.WasmGCClassConsumerContext;
|
||||||
import org.teavm.backend.wasm.gc.WasmGCDependencies;
|
import org.teavm.backend.wasm.gc.WasmGCDependencies;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.LaxMallocInitializerContributor;
|
||||||
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
||||||
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
|
||||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory;
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory;
|
||||||
|
@ -47,6 +48,7 @@ import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsics;
|
||||||
import org.teavm.backend.wasm.model.WasmCustomSection;
|
import org.teavm.backend.wasm.model.WasmCustomSection;
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
import org.teavm.backend.wasm.model.WasmLocal;
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmMemorySegment;
|
||||||
import org.teavm.backend.wasm.model.WasmModule;
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
import org.teavm.backend.wasm.model.WasmTag;
|
import org.teavm.backend.wasm.model.WasmTag;
|
||||||
import org.teavm.backend.wasm.model.WasmType;
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
|
@ -72,10 +74,13 @@ import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
import org.teavm.model.lowlevel.LowLevelNullCheckFilter;
|
||||||
import org.teavm.model.transformation.BoundCheckInsertion;
|
import org.teavm.model.transformation.BoundCheckInsertion;
|
||||||
import org.teavm.model.transformation.NullCheckFilter;
|
import org.teavm.model.transformation.NullCheckFilter;
|
||||||
import org.teavm.model.transformation.NullCheckInsertion;
|
import org.teavm.model.transformation.NullCheckInsertion;
|
||||||
import org.teavm.model.util.VariableCategoryProvider;
|
import org.teavm.model.util.VariableCategoryProvider;
|
||||||
|
import org.teavm.runtime.LaxMalloc;
|
||||||
import org.teavm.vm.BuildTarget;
|
import org.teavm.vm.BuildTarget;
|
||||||
import org.teavm.vm.TeaVMTarget;
|
import org.teavm.vm.TeaVMTarget;
|
||||||
import org.teavm.vm.TeaVMTargetController;
|
import org.teavm.vm.TeaVMTargetController;
|
||||||
|
@ -83,7 +88,8 @@ import org.teavm.vm.spi.TeaVMHostExtension;
|
||||||
|
|
||||||
public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
private TeaVMTargetController controller;
|
private TeaVMTargetController controller;
|
||||||
private NullCheckInsertion nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
private Characteristics characteristics;
|
||||||
|
private NullCheckInsertion nullCheckInsertion;
|
||||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
private boolean strict;
|
private boolean strict;
|
||||||
private boolean obfuscated;
|
private boolean obfuscated;
|
||||||
|
@ -99,6 +105,9 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
private List<WasmGCCustomGeneratorFactory> customGeneratorFactories = new ArrayList<>();
|
private List<WasmGCCustomGeneratorFactory> customGeneratorFactories = new ArrayList<>();
|
||||||
private EntryPointTransformation entryPointTransformation = new EntryPointTransformation();
|
private EntryPointTransformation entryPointTransformation = new EntryPointTransformation();
|
||||||
private List<WasmGCClassConsumer> classConsumers = new ArrayList<>();
|
private List<WasmGCClassConsumer> classConsumers = new ArrayList<>();
|
||||||
|
private boolean enableDirectMallocSupport;
|
||||||
|
private int directMallocMinHeapSize = 0x10000;
|
||||||
|
private int directMallocMaxHeapSize = 0x10000000;
|
||||||
|
|
||||||
public void setObfuscated(boolean obfuscated) {
|
public void setObfuscated(boolean obfuscated) {
|
||||||
this.obfuscated = obfuscated;
|
this.obfuscated = obfuscated;
|
||||||
|
@ -128,6 +137,18 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
this.sourceMapLocation = sourceMapLocation;
|
this.sourceMapLocation = sourceMapLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEnableDirectMallocSupport(boolean enable) {
|
||||||
|
this.enableDirectMallocSupport = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirectMallocMinHeapSize(int minHeapSize) {
|
||||||
|
this.directMallocMinHeapSize = WasmRuntime.align(minHeapSize, WasmHeap.PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirectMallocMaxHeapSize(int maxHeapSize) {
|
||||||
|
this.directMallocMaxHeapSize = WasmRuntime.align(maxHeapSize, WasmHeap.PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addIntrinsicFactory(WasmGCIntrinsicFactory intrinsicFactory) {
|
public void addIntrinsicFactory(WasmGCIntrinsicFactory intrinsicFactory) {
|
||||||
intrinsicFactories.add(intrinsicFactory);
|
intrinsicFactories.add(intrinsicFactory);
|
||||||
|
@ -161,6 +182,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
@Override
|
@Override
|
||||||
public void setController(TeaVMTargetController controller) {
|
public void setController(TeaVMTargetController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||||
|
nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -194,6 +217,9 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
var deps = new WasmGCDependencies(dependencyAnalyzer);
|
var deps = new WasmGCDependencies(dependencyAnalyzer);
|
||||||
deps.contribute();
|
deps.contribute();
|
||||||
deps.contributeStandardExports();
|
deps.contributeStandardExports();
|
||||||
|
if (enableDirectMallocSupport) {
|
||||||
|
deps.contributeDirectMalloc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -246,6 +272,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
controller.getClassInitializerInfo(),
|
controller.getClassInitializerInfo(),
|
||||||
controller.getDependencyInfo(),
|
controller.getDependencyInfo(),
|
||||||
controller.getDiagnostics(),
|
controller.getDiagnostics(),
|
||||||
|
characteristics,
|
||||||
customGenerators,
|
customGenerators,
|
||||||
intrinsics,
|
intrinsics,
|
||||||
customTypeMapperFactories,
|
customTypeMapperFactories,
|
||||||
|
@ -282,9 +309,27 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
refQueueSupplyFunction.setExportName("teavm.reportGarbageCollectedValue");
|
refQueueSupplyFunction.setExportName("teavm.reportGarbageCollectedValue");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(enableDirectMallocSupport) {
|
||||||
|
var laxMallocClinitRef = new MethodReference(LaxMalloc.class, "<clinit>", void.class);
|
||||||
|
if (controller.getDependencyInfo().getMethod(laxMallocClinitRef) != null) {
|
||||||
|
var laxMallocClinit = declarationsGenerator.functions().forStaticMethod(laxMallocClinitRef);
|
||||||
|
declarationsGenerator.addEarlyInitializerContributor(new LaxMallocInitializerContributor(laxMallocClinit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
moduleGenerator.generate();
|
moduleGenerator.generate();
|
||||||
customGenerators.contributeToModule(module);
|
customGenerators.contributeToModule(module);
|
||||||
generateExceptionExports(declarationsGenerator);
|
generateExceptionExports(declarationsGenerator);
|
||||||
|
if (enableDirectMallocSupport) {
|
||||||
|
var heapSegmentStart = 0;
|
||||||
|
if (!module.getSegments().isEmpty()) {
|
||||||
|
var lastSegment = module.getSegments().get(module.getSegments().size() - 1);
|
||||||
|
heapSegmentStart = WasmRuntime.align(lastSegment.getOffset()
|
||||||
|
+ lastSegment.getLength(), WasmHeap.PAGE_SIZE);
|
||||||
|
}
|
||||||
|
intrinsics.setupLaxMallocHeap(heapSegmentStart, heapSegmentStart + directMallocMinHeapSize,
|
||||||
|
heapSegmentStart + directMallocMaxHeapSize);
|
||||||
|
}
|
||||||
adjustModuleMemory(module);
|
adjustModuleMemory(module);
|
||||||
|
|
||||||
emitWasmFile(module, buildTarget, outputName, debugInfoBuilder);
|
emitWasmFile(module, buildTarget, outputName, debugInfoBuilder);
|
||||||
|
@ -390,9 +435,16 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var pages = (memorySize - 1) / WasmHeap.PAGE_SIZE + 1;
|
if (enableDirectMallocSupport) {
|
||||||
module.setMinMemorySize(pages);
|
var minPages = (memorySize - 1) / WasmHeap.PAGE_SIZE + 1;
|
||||||
module.setMaxMemorySize(pages);
|
var maxPages = minPages + (directMallocMaxHeapSize - 1) / WasmHeap.PAGE_SIZE + 1;
|
||||||
|
module.setMinMemorySize(minPages);
|
||||||
|
module.setMaxMemorySize(maxPages);
|
||||||
|
} else {
|
||||||
|
var pages = (memorySize - 1) / WasmHeap.PAGE_SIZE + 1;
|
||||||
|
module.setMinMemorySize(pages);
|
||||||
|
module.setMaxMemorySize(pages);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName,
|
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName,
|
||||||
|
|
|
@ -22,7 +22,9 @@ import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.runtime.LaxMalloc;
|
||||||
|
|
||||||
public class WasmGCDependencies {
|
public class WasmGCDependencies {
|
||||||
private DependencyAnalyzer analyzer;
|
private DependencyAnalyzer analyzer;
|
||||||
|
@ -127,4 +129,11 @@ public class WasmGCDependencies {
|
||||||
private void contributeString() {
|
private void contributeString() {
|
||||||
analyzer.addDependencyListener(new StringInternDependencySupport());
|
analyzer.addDependencyListener(new StringInternDependencySupport());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void contributeDirectMalloc() {
|
||||||
|
analyzer.linkMethod(new MethodReference(LaxMalloc.class, "<clinit>", void.class)).use();
|
||||||
|
analyzer.linkMethod(new MethodReference(LaxMalloc.class, "laxMalloc", int.class, Address.class)).use();
|
||||||
|
analyzer.linkMethod(new MethodReference(LaxMalloc.class, "laxCalloc", int.class, Address.class)).use();
|
||||||
|
analyzer.linkMethod(new MethodReference(LaxMalloc.class, "laxFree", Address.class, void.class)).use();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 lax1dude.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.wasm.generate.gc;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
|
|
||||||
|
public class LaxMallocInitializerContributor implements WasmGCInitializerContributor {
|
||||||
|
|
||||||
|
private final WasmFunction clinit;
|
||||||
|
|
||||||
|
public LaxMallocInitializerContributor(WasmFunction clinit) {
|
||||||
|
this.clinit = clinit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contributeToInitializerDefinitions(WasmFunction function) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contributeToInitializer(WasmFunction function) {
|
||||||
|
function.getBody().add(new WasmCall(clinit));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
import org.teavm.model.analysis.ClassMetadataRequirements;
|
import org.teavm.model.analysis.ClassMetadataRequirements;
|
||||||
import org.teavm.model.classes.TagRegistry;
|
import org.teavm.model.classes.TagRegistry;
|
||||||
import org.teavm.model.classes.VirtualTableBuilder;
|
import org.teavm.model.classes.VirtualTableBuilder;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
|
||||||
public class WasmGCDeclarationsGenerator {
|
public class WasmGCDeclarationsGenerator {
|
||||||
public final ClassHierarchy hierarchy;
|
public final ClassHierarchy hierarchy;
|
||||||
|
@ -61,6 +62,7 @@ public class WasmGCDeclarationsGenerator {
|
||||||
ClassInitializerInfo classInitializerInfo,
|
ClassInitializerInfo classInitializerInfo,
|
||||||
DependencyInfo dependencyInfo,
|
DependencyInfo dependencyInfo,
|
||||||
Diagnostics diagnostics,
|
Diagnostics diagnostics,
|
||||||
|
Characteristics characteristics,
|
||||||
WasmGCCustomGeneratorProvider customGenerators,
|
WasmGCCustomGeneratorProvider customGenerators,
|
||||||
WasmGCIntrinsicProvider intrinsics,
|
WasmGCIntrinsicProvider intrinsics,
|
||||||
List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories,
|
List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories,
|
||||||
|
@ -83,6 +85,7 @@ public class WasmGCDeclarationsGenerator {
|
||||||
functionTypes,
|
functionTypes,
|
||||||
names,
|
names,
|
||||||
diagnostics,
|
diagnostics,
|
||||||
|
characteristics,
|
||||||
customGenerators,
|
customGenerators,
|
||||||
intrinsics,
|
intrinsics,
|
||||||
strict,
|
strict,
|
||||||
|
@ -178,4 +181,8 @@ public class WasmGCDeclarationsGenerator {
|
||||||
public void addToInitializer(Consumer<WasmFunction> contributor) {
|
public void addToInitializer(Consumer<WasmFunction> contributor) {
|
||||||
methodGenerator.getGenerationContext().addToInitializer(contributor);
|
methodGenerator.getGenerationContext().addToInitializer(contributor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addEarlyInitializerContributor(WasmGCInitializerContributor contributor) {
|
||||||
|
initializerContributors.add(contributor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.teavm.backend.wasm.model.WasmModule;
|
||||||
import org.teavm.backend.wasm.model.WasmPackedType;
|
import org.teavm.backend.wasm.model.WasmPackedType;
|
||||||
import org.teavm.backend.wasm.model.WasmStorageType;
|
import org.teavm.backend.wasm.model.WasmStorageType;
|
||||||
import org.teavm.backend.wasm.model.WasmType;
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
@ -127,12 +128,17 @@ public class WasmGCTypeMapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
var cls = classes.get(className);
|
if (className.equals(Address.class.getName())) {
|
||||||
if (cls == null) {
|
result = WasmType.INT32;
|
||||||
className = "java.lang.Object";
|
typeCache.put(className, result);
|
||||||
|
} else {
|
||||||
|
var cls = classes.get(className);
|
||||||
|
if (cls == null) {
|
||||||
|
className = "java.lang.Object";
|
||||||
|
}
|
||||||
|
result = classInfoProvider.getClassInfo(className).getType();
|
||||||
|
typeCache.put(className, result);
|
||||||
}
|
}
|
||||||
result = classInfoProvider.getClassInfo(className).getType();
|
|
||||||
typeCache.put(className, result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
|
||||||
public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
private WasmModule module;
|
private WasmModule module;
|
||||||
|
@ -69,6 +70,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
private String entryPoint;
|
private String entryPoint;
|
||||||
private Consumer<WasmGCInitializerContributor> initializerContributors;
|
private Consumer<WasmGCInitializerContributor> initializerContributors;
|
||||||
private Diagnostics diagnostics;
|
private Diagnostics diagnostics;
|
||||||
|
private Characteristics characteristics;
|
||||||
|
|
||||||
public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables,
|
public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables,
|
||||||
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes,
|
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes,
|
||||||
|
@ -78,7 +80,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics,
|
WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics,
|
||||||
WasmGCNameProvider names, boolean strict, String entryPoint,
|
WasmGCNameProvider names, boolean strict, String entryPoint,
|
||||||
Consumer<WasmGCInitializerContributor> initializerContributors,
|
Consumer<WasmGCInitializerContributor> initializerContributors,
|
||||||
Diagnostics diagnostics) {
|
Diagnostics diagnostics, Characteristics characteristics) {
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.virtualTables = virtualTables;
|
this.virtualTables = virtualTables;
|
||||||
this.typeMapper = typeMapper;
|
this.typeMapper = typeMapper;
|
||||||
|
@ -98,6 +100,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
this.entryPoint = entryPoint;
|
this.entryPoint = entryPoint;
|
||||||
this.initializerContributors = initializerContributors;
|
this.initializerContributors = initializerContributors;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
|
this.characteristics = characteristics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WasmGCClassInfoProvider classInfoProvider() {
|
public WasmGCClassInfoProvider classInfoProvider() {
|
||||||
|
@ -210,6 +213,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Characteristics characteristics() {
|
||||||
|
return characteristics;
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<String> getInterfaceImplementors(String className) {
|
public Collection<String> getInterfaceImplementors(String className) {
|
||||||
if (interfaceImplementors == null) {
|
if (interfaceImplementors == null) {
|
||||||
fillInterfaceImplementors();
|
fillInterfaceImplementors();
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generate.gc.methods;
|
package org.teavm.backend.wasm.generate.gc.methods;
|
||||||
|
|
||||||
|
import static org.teavm.model.lowlevel.ExceptionHandlingUtil.isManagedMethodCall;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -97,6 +99,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
private WasmGCGenerationUtil generationUtil;
|
private WasmGCGenerationUtil generationUtil;
|
||||||
private WasmType expectedType;
|
private WasmType expectedType;
|
||||||
private PreciseTypeInference types;
|
private PreciseTypeInference types;
|
||||||
|
private boolean managed;
|
||||||
|
|
||||||
public WasmGCGenerationVisitor(WasmGCGenerationContext context, MethodReference currentMethod,
|
public WasmGCGenerationVisitor(WasmGCGenerationContext context, MethodReference currentMethod,
|
||||||
WasmFunction function, int firstVariable, boolean async, PreciseTypeInference types) {
|
WasmFunction function, int firstVariable, boolean async, PreciseTypeInference types) {
|
||||||
|
@ -104,6 +107,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider());
|
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider());
|
||||||
this.types = types;
|
this.types = types;
|
||||||
|
managed = context.characteristics().isManaged(currentMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -125,7 +129,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isManaged() {
|
protected boolean isManaged() {
|
||||||
return true;
|
return managed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,7 +139,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isManagedCall(MethodReference method) {
|
protected boolean isManagedCall(MethodReference method) {
|
||||||
return false;
|
return isManagedMethodCall(context.characteristics(), method);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -61,6 +61,7 @@ import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.analysis.ClassInitializerInfo;
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.util.RegisterAllocator;
|
import org.teavm.model.util.RegisterAllocator;
|
||||||
|
|
||||||
public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
|
@ -74,6 +75,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
private WasmGCSupertypeFunctionProvider supertypeFunctions;
|
private WasmGCSupertypeFunctionProvider supertypeFunctions;
|
||||||
public final WasmGCNameProvider names;
|
public final WasmGCNameProvider names;
|
||||||
private Diagnostics diagnostics;
|
private Diagnostics diagnostics;
|
||||||
|
private Characteristics characteristics;
|
||||||
private WasmGCTypeMapper typeMapper;
|
private WasmGCTypeMapper typeMapper;
|
||||||
private WasmGCCustomGeneratorProvider customGenerators;
|
private WasmGCCustomGeneratorProvider customGenerators;
|
||||||
private WasmGCIntrinsicProvider intrinsics;
|
private WasmGCIntrinsicProvider intrinsics;
|
||||||
|
@ -101,6 +103,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
WasmFunctionTypes functionTypes,
|
WasmFunctionTypes functionTypes,
|
||||||
WasmGCNameProvider names,
|
WasmGCNameProvider names,
|
||||||
Diagnostics diagnostics,
|
Diagnostics diagnostics,
|
||||||
|
Characteristics characteristics,
|
||||||
WasmGCCustomGeneratorProvider customGenerators,
|
WasmGCCustomGeneratorProvider customGenerators,
|
||||||
WasmGCIntrinsicProvider intrinsics,
|
WasmGCIntrinsicProvider intrinsics,
|
||||||
boolean strict,
|
boolean strict,
|
||||||
|
@ -116,6 +119,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
this.functionTypes = functionTypes;
|
this.functionTypes = functionTypes;
|
||||||
this.names = names;
|
this.names = names;
|
||||||
this.diagnostics = diagnostics;
|
this.diagnostics = diagnostics;
|
||||||
|
this.characteristics = characteristics;
|
||||||
this.customGenerators = customGenerators;
|
this.customGenerators = customGenerators;
|
||||||
this.intrinsics = intrinsics;
|
this.intrinsics = intrinsics;
|
||||||
this.strict = strict;
|
this.strict = strict;
|
||||||
|
@ -367,7 +371,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
strict,
|
strict,
|
||||||
entryPoint,
|
entryPoint,
|
||||||
initializerContributors,
|
initializerContributors,
|
||||||
diagnostics
|
diagnostics,
|
||||||
|
characteristics
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.wasm.intrinsics.gc;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.backend.wasm.WasmRuntime;
|
||||||
|
import org.teavm.backend.wasm.model.WasmNumType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmConversion;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt64Subtype;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStoreFloat32;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStoreFloat64;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
public class AddressIntrinsic implements WasmGCIntrinsic {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext manager) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "toInt":
|
||||||
|
case "toStructure":
|
||||||
|
return manager.generate(invocation.getArguments().get(0));
|
||||||
|
case "toLong": {
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(0));
|
||||||
|
return new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, false, value);
|
||||||
|
}
|
||||||
|
case "fromInt":
|
||||||
|
return manager.generate(invocation.getArguments().get(0));
|
||||||
|
case "fromLong": {
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(0));
|
||||||
|
return new WasmConversion(WasmNumType.INT64, WasmNumType.INT32, false, value);
|
||||||
|
}
|
||||||
|
case "add": {
|
||||||
|
WasmExpression base = manager.generate(invocation.getArguments().get(0));
|
||||||
|
if (invocation.getMethod().parameterCount() == 1) {
|
||||||
|
WasmExpression offset = manager.generate(invocation.getArguments().get(1));
|
||||||
|
if (invocation.getMethod().parameterType(0) == ValueType.LONG) {
|
||||||
|
offset = new WasmConversion(WasmNumType.INT64, WasmNumType.INT32, false, offset);
|
||||||
|
}
|
||||||
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "getByte":
|
||||||
|
return new WasmLoadInt32(1, manager.generate(invocation.getArguments().get(0)),
|
||||||
|
WasmInt32Subtype.INT8);
|
||||||
|
case "getShort":
|
||||||
|
return new WasmLoadInt32(2, manager.generate(invocation.getArguments().get(0)),
|
||||||
|
WasmInt32Subtype.INT16);
|
||||||
|
case "getChar":
|
||||||
|
return new WasmLoadInt32(2, manager.generate(invocation.getArguments().get(0)),
|
||||||
|
WasmInt32Subtype.UINT16);
|
||||||
|
case "getAddress":
|
||||||
|
case "getInt":
|
||||||
|
return new WasmLoadInt32(4, manager.generate(invocation.getArguments().get(0)),
|
||||||
|
WasmInt32Subtype.INT32);
|
||||||
|
case "getLong":
|
||||||
|
return new WasmLoadInt64(8, manager.generate(invocation.getArguments().get(0)),
|
||||||
|
WasmInt64Subtype.INT64);
|
||||||
|
case "getFloat":
|
||||||
|
return new WasmLoadFloat32(4, manager.generate(invocation.getArguments().get(0)));
|
||||||
|
case "getDouble":
|
||||||
|
return new WasmLoadFloat64(8, manager.generate(invocation.getArguments().get(0)));
|
||||||
|
case "putByte": {
|
||||||
|
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||||
|
return new WasmStoreInt32(1, address, value, WasmInt32Subtype.INT8);
|
||||||
|
}
|
||||||
|
case "putShort": {
|
||||||
|
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||||
|
return new WasmStoreInt32(2, address, value, WasmInt32Subtype.INT16);
|
||||||
|
}
|
||||||
|
case "putChar": {
|
||||||
|
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||||
|
return new WasmStoreInt32(2, address, value, WasmInt32Subtype.UINT16);
|
||||||
|
}
|
||||||
|
case "putAddress":
|
||||||
|
case "putInt": {
|
||||||
|
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||||
|
return new WasmStoreInt32(4, address, value, WasmInt32Subtype.INT32);
|
||||||
|
}
|
||||||
|
case "putLong": {
|
||||||
|
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||||
|
return new WasmStoreInt64(8, address, value, WasmInt64Subtype.INT64);
|
||||||
|
}
|
||||||
|
case "putFloat": {
|
||||||
|
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||||
|
return new WasmStoreFloat32(4, address, value);
|
||||||
|
}
|
||||||
|
case "putDouble": {
|
||||||
|
WasmExpression address = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression value = manager.generate(invocation.getArguments().get(1));
|
||||||
|
return new WasmStoreFloat64(8, address, value);
|
||||||
|
}
|
||||||
|
case "sizeOf":
|
||||||
|
return new WasmInt32Constant(4);
|
||||||
|
case "align": {
|
||||||
|
MethodReference delegate = new MethodReference(WasmRuntime.class.getName(),
|
||||||
|
invocation.getMethod().getDescriptor());
|
||||||
|
WasmCall call = new WasmCall(manager.functions().forStaticMethod(delegate));
|
||||||
|
call.getArguments().addAll(invocation.getArguments().stream()
|
||||||
|
.map(arg -> manager.generate(arg))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
case "isLessThan":
|
||||||
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_UNSIGNED,
|
||||||
|
manager.generate(invocation.getArguments().get(0)),
|
||||||
|
manager.generate(invocation.getArguments().get(1)));
|
||||||
|
case "diff": {
|
||||||
|
WasmExpression result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
|
||||||
|
manager.generate(invocation.getArguments().get(0)),
|
||||||
|
manager.generate(invocation.getArguments().get(1))
|
||||||
|
);
|
||||||
|
result = new WasmConversion(WasmNumType.INT32, WasmNumType.INT64, true, result);
|
||||||
|
result.setLocation(invocation.getLocation());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 lax1dude.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.wasm.intrinsics.gc;
|
||||||
|
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCopy;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmFill;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
||||||
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.runtime.LaxMalloc;
|
||||||
|
|
||||||
|
public class DirectMallocIntrinsic implements WasmGCIntrinsic {
|
||||||
|
private static final MethodReference LAX_MALLOC = new MethodReference(LaxMalloc.class, "laxMalloc", int.class,
|
||||||
|
Address.class);
|
||||||
|
private static final MethodReference LAX_CALLOC = new MethodReference(LaxMalloc.class, "laxCalloc", int.class,
|
||||||
|
Address.class);
|
||||||
|
private static final MethodReference LAX_FREE = new MethodReference(LaxMalloc.class, "laxFree", Address.class,
|
||||||
|
void.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext manager) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "malloc": {
|
||||||
|
var function = manager.functions().forStaticMethod(LAX_MALLOC);
|
||||||
|
var call = new WasmCall(function);
|
||||||
|
call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
case "calloc": {
|
||||||
|
var function = manager.functions().forStaticMethod(LAX_CALLOC);
|
||||||
|
var call = new WasmCall(function);
|
||||||
|
call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
case "free": {
|
||||||
|
var function = manager.functions().forStaticMethod(LAX_FREE);
|
||||||
|
var call = new WasmCall(function);
|
||||||
|
call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
case "memcpy": {
|
||||||
|
var copy = new WasmCopy();
|
||||||
|
copy.setDestinationIndex(manager.generate(invocation.getArguments().get(0)));
|
||||||
|
copy.setSourceIndex(manager.generate(invocation.getArguments().get(1)));
|
||||||
|
copy.setCount(manager.generate(invocation.getArguments().get(2)));
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
case "memset": {
|
||||||
|
var fill = new WasmFill();
|
||||||
|
fill.setIndex(manager.generate(invocation.getArguments().get(0)));
|
||||||
|
fill.setValue(manager.generate(invocation.getArguments().get(1)));
|
||||||
|
fill.setCount(manager.generate(invocation.getArguments().get(2)));
|
||||||
|
return fill;
|
||||||
|
}
|
||||||
|
case "zmemset": {
|
||||||
|
var fill = new WasmFill();
|
||||||
|
fill.setIndex(manager.generate(invocation.getArguments().get(0)));
|
||||||
|
fill.setValue(new WasmInt32Constant(0));
|
||||||
|
fill.setCount(manager.generate(invocation.getArguments().get(1)));
|
||||||
|
return fill;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return new WasmUnreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 lax1dude.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.backend.wasm.intrinsics.gc;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
|
||||||
|
|
||||||
|
public class LaxMallocIntrinsic implements WasmGCIntrinsic {
|
||||||
|
|
||||||
|
private final List<LaxMallocHeapMapper> addressList = new ArrayList<>();
|
||||||
|
private final List<WasmInt32Constant> minAddrConstants = new ArrayList<>();
|
||||||
|
private final List<WasmInt32Constant> maxAddrConstants = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "addrHeap": {
|
||||||
|
WasmExpression value = context.generate(invocation.getArguments().get(0));
|
||||||
|
if (value instanceof WasmInt32Constant) {
|
||||||
|
// if addrHeap is passed a constant i32, add the heap offset at compile time
|
||||||
|
final int memOffset = ((WasmInt32Constant) value).getValue();
|
||||||
|
WasmInt32Constant ret = new WasmInt32Constant(0);
|
||||||
|
addressList.add(heapLoc -> {
|
||||||
|
ret.setValue(heapLoc + memOffset);
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
} else {
|
||||||
|
WasmInt32Constant heapLocConst = new WasmInt32Constant(0);
|
||||||
|
WasmExpression calcOffset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||||
|
heapLocConst, value);
|
||||||
|
addressList.add(heapLocConst::setValue);
|
||||||
|
return calcOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "growHeapOuter": {
|
||||||
|
return new WasmMemoryGrow(context.generate(invocation.getArguments().get(0)));
|
||||||
|
}
|
||||||
|
case "getHeapMinAddr": {
|
||||||
|
WasmInt32Constant ret = new WasmInt32Constant(0);
|
||||||
|
minAddrConstants.add(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
case "getHeapMaxAddr": {
|
||||||
|
WasmInt32Constant ret = new WasmInt32Constant(0);
|
||||||
|
maxAddrConstants.add(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeapLocation(int heapLoc) {
|
||||||
|
for (LaxMallocHeapMapper mapper : addressList) {
|
||||||
|
mapper.setHeapLocation(heapLoc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface LaxMallocHeapMapper {
|
||||||
|
void setHeapLocation(int heapLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeapMinAddr(int heapSegmentMinAddr) {
|
||||||
|
for (WasmInt32Constant ct : minAddrConstants) {
|
||||||
|
ct.setValue(heapSegmentMinAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeapMaxAddr(int heapSegmentMaxAddr) {
|
||||||
|
for (WasmInt32Constant ct : maxAddrConstants) {
|
||||||
|
ct.setValue(heapSegmentMaxAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,15 +25,19 @@ import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
import org.teavm.backend.wasm.runtime.StringInternPool;
|
import org.teavm.backend.wasm.runtime.StringInternPool;
|
||||||
import org.teavm.backend.wasm.runtime.gc.WasmGCResources;
|
import org.teavm.backend.wasm.runtime.gc.WasmGCResources;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.interop.DirectMalloc;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.runtime.LaxMalloc;
|
||||||
|
|
||||||
public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
||||||
private Map<MethodReference, IntrinsicContainer> intrinsics = new HashMap<>();
|
private Map<MethodReference, IntrinsicContainer> intrinsics = new HashMap<>();
|
||||||
private List<WasmGCIntrinsicFactory> factories;
|
private List<WasmGCIntrinsicFactory> factories;
|
||||||
private ClassReaderSource classes;
|
private ClassReaderSource classes;
|
||||||
private ServiceRepository services;
|
private ServiceRepository services;
|
||||||
|
private LaxMallocIntrinsic laxMallocIntrinsic;
|
||||||
|
|
||||||
public WasmGCIntrinsics(ClassReaderSource classes, ServiceRepository services,
|
public WasmGCIntrinsics(ClassReaderSource classes, ServiceRepository services,
|
||||||
List<WasmGCIntrinsicFactory> factories, Map<MethodReference, WasmGCIntrinsic> customIntrinsics) {
|
List<WasmGCIntrinsicFactory> factories, Map<MethodReference, WasmGCIntrinsic> customIntrinsics) {
|
||||||
|
@ -51,6 +55,9 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
||||||
fillArray();
|
fillArray();
|
||||||
fillString();
|
fillString();
|
||||||
fillResources();
|
fillResources();
|
||||||
|
fillDirectMalloc();
|
||||||
|
fillLaxMalloc();
|
||||||
|
fillAddress();
|
||||||
for (var entry : customIntrinsics.entrySet()) {
|
for (var entry : customIntrinsics.entrySet()) {
|
||||||
add(entry.getKey(), entry.getValue());
|
add(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
@ -166,6 +173,58 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
||||||
add(new MethodReference(WasmGCResources.class, "readSingleByte", int.class, int.class), intrinsic);
|
add(new MethodReference(WasmGCResources.class, "readSingleByte", int.class, int.class), intrinsic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillDirectMalloc() {
|
||||||
|
var intrinsic = new DirectMallocIntrinsic();
|
||||||
|
add(new MethodReference(DirectMalloc.class, "malloc", int.class, Address.class), intrinsic);
|
||||||
|
add(new MethodReference(DirectMalloc.class, "calloc", int.class, Address.class), intrinsic);
|
||||||
|
add(new MethodReference(DirectMalloc.class, "free", Address.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(DirectMalloc.class, "memcpy", Address.class, Address.class, int.class, void.class),
|
||||||
|
intrinsic);
|
||||||
|
add(new MethodReference(DirectMalloc.class, "memset", Address.class, int.class, int.class, void.class),
|
||||||
|
intrinsic);
|
||||||
|
add(new MethodReference(DirectMalloc.class, "zmemset", Address.class, int.class, void.class), intrinsic);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillLaxMalloc() {
|
||||||
|
laxMallocIntrinsic = new LaxMallocIntrinsic();
|
||||||
|
add(new MethodReference(LaxMalloc.class, "addrHeap", int.class, Address.class), laxMallocIntrinsic);
|
||||||
|
add(new MethodReference(LaxMalloc.class, "growHeapOuter", int.class, int.class), laxMallocIntrinsic);
|
||||||
|
add(new MethodReference(LaxMalloc.class, "getHeapMinAddr", Address.class), laxMallocIntrinsic);
|
||||||
|
add(new MethodReference(LaxMalloc.class, "getHeapMaxAddr", Address.class), laxMallocIntrinsic);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillAddress() {
|
||||||
|
var intrinsic = new AddressIntrinsic();
|
||||||
|
add(new MethodReference(Address.class, "add", int.class, Address.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "add", long.class, Address.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "isLessThan", Address.class, boolean.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "toInt", int.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "toLong", long.class), intrinsic);
|
||||||
|
//add(new MethodReference(Address.class, "toStructure", ?????), intrinsic); //TODO
|
||||||
|
add(new MethodReference(Address.class, "getByte", byte.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putByte", byte.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "getChar", char.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putChar", char.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "getShort", short.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putShort", short.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "getInt", int.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putInt", int.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "getLong", long.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putLong", long.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "getFloat", float.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putFloat", float.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "getDouble", double.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putDouble", double.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "getAddress", Address.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "putAddress", Address.class, void.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "fromInt", int.class, Address.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "fromLong", long.class, Address.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "align", Address.class, int.class, Address.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "sizeOf", int.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "add", Class.class, int.class), intrinsic);
|
||||||
|
add(new MethodReference(Address.class, "diff", Address.class, long.class), intrinsic);
|
||||||
|
}
|
||||||
|
|
||||||
private void add(MethodReference methodRef, WasmGCIntrinsic intrinsic) {
|
private void add(MethodReference methodRef, WasmGCIntrinsic intrinsic) {
|
||||||
intrinsics.put(methodRef, new IntrinsicContainer(intrinsic));
|
intrinsics.put(methodRef, new IntrinsicContainer(intrinsic));
|
||||||
}
|
}
|
||||||
|
@ -187,6 +246,12 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
|
||||||
return result.intrinsic;
|
return result.intrinsic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setupLaxMallocHeap(int heapAddr, int heapSegmentMinAddr, int heapSegmentMaxAddr) {
|
||||||
|
laxMallocIntrinsic.setHeapLocation(heapAddr);
|
||||||
|
laxMallocIntrinsic.setHeapMinAddr(heapSegmentMinAddr);
|
||||||
|
laxMallocIntrinsic.setHeapMaxAddr(heapSegmentMaxAddr);
|
||||||
|
}
|
||||||
|
|
||||||
static class IntrinsicContainer {
|
static class IntrinsicContainer {
|
||||||
final WasmGCIntrinsic intrinsic;
|
final WasmGCIntrinsic intrinsic;
|
||||||
|
|
||||||
|
|
643
core/src/main/java/org/teavm/runtime/LaxMalloc.java
Normal file
643
core/src/main/java/org/teavm/runtime/LaxMalloc.java
Normal file
|
@ -0,0 +1,643 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 lax1dude.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.interop.DirectMalloc;
|
||||||
|
import org.teavm.interop.Import;
|
||||||
|
import org.teavm.interop.StaticInit;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linear memory allocator for creating "direct buffers" in WASM GC<br><br>
|
||||||
|
*
|
||||||
|
* DO NOT USE IN LEGACY WASM BACKEND!!! Make a regular byte array, and use Address.ofData()<br><br>
|
||||||
|
*
|
||||||
|
* Similar to dlmalloc and emmalloc (emscripten's malloc)<br><br>
|
||||||
|
*
|
||||||
|
* bad things will happen if you free an address that was never allocated
|
||||||
|
*
|
||||||
|
* @author lax1dude
|
||||||
|
*/
|
||||||
|
@Unmanaged
|
||||||
|
@StaticInit
|
||||||
|
public final class LaxMalloc {
|
||||||
|
|
||||||
|
private LaxMalloc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int SIZEOF_PTR = 4;
|
||||||
|
private static final int SIZEOF_PTR_SH = 2;
|
||||||
|
|
||||||
|
private static final int MIN_ALLOC_SIZE = 8;
|
||||||
|
|
||||||
|
// Address where we store the WebAssembly.Memory limit (32 bit int)
|
||||||
|
private static final int ADDR_HEAP_OUTER_LIMIT = 0;
|
||||||
|
|
||||||
|
// Address where we store the current heap limit (32 bit int)
|
||||||
|
private static final int ADDR_HEAP_INNER_LIMIT = 4;
|
||||||
|
|
||||||
|
// Address where we store the bitmask of free chunk lists (64 bit int)
|
||||||
|
private static final int ADDR_HEAP_BUCKETS_FREE_MASK = 8;
|
||||||
|
|
||||||
|
// Address to the list of 64 pointers to the beginnings of the 64 buckets
|
||||||
|
private static final int ADDR_HEAP_BUCKETS_START = 16;
|
||||||
|
|
||||||
|
// Beginning of the first chunk of the heap
|
||||||
|
private static final int ADDR_HEAP_DATA_START = 272;
|
||||||
|
|
||||||
|
// Intrinsic function to get an address in the heap segment
|
||||||
|
private static native Address addrHeap(int offset);
|
||||||
|
|
||||||
|
// Intrinsic function to grow the heap segment
|
||||||
|
private static native int growHeapOuter(int chunks);
|
||||||
|
|
||||||
|
// Intrinsic function to get the minimum direct malloc heap segment ending address
|
||||||
|
private static native Address getHeapMinAddr();
|
||||||
|
|
||||||
|
// Intrinsic function to get the maximum direct malloc heap segment ending address
|
||||||
|
private static native Address getHeapMaxAddr();
|
||||||
|
|
||||||
|
// Function called to resize the JavaScript typed arrays wrapping the WebAssembly.Memory
|
||||||
|
@Import(name = "notifyHeapResized")
|
||||||
|
private static native void notifyHeapResized();
|
||||||
|
|
||||||
|
static {
|
||||||
|
int initialGrowAmount = getHeapMinAddr().toInt() >>> 16;
|
||||||
|
if (growHeapOuter(initialGrowAmount) == -1) {
|
||||||
|
initialGrowAmount = 1;
|
||||||
|
if (growHeapOuter(initialGrowAmount) == -1) {
|
||||||
|
//TODO: Handle failure to initialize fallback 64KiB heap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero out the control region
|
||||||
|
DirectMalloc.zmemset(addrHeap(0), ADDR_HEAP_DATA_START);
|
||||||
|
// initialize heap limit
|
||||||
|
addrHeap(ADDR_HEAP_INNER_LIMIT).putAddress(addrHeap(ADDR_HEAP_DATA_START));
|
||||||
|
addrHeap(ADDR_HEAP_OUTER_LIMIT).putAddress(addrHeap(initialGrowAmount << 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* malloc implementation
|
||||||
|
*/
|
||||||
|
public static Address laxMalloc(int sizeBytes) {
|
||||||
|
return laxAlloc(sizeBytes, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* calloc implementation (zeroed malloc)
|
||||||
|
*/
|
||||||
|
public static Address laxCalloc(int sizeBytes) {
|
||||||
|
return laxAlloc(sizeBytes, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Address laxAlloc(int sizeBytes, boolean cleared) {
|
||||||
|
if (sizeBytes <= 0) {
|
||||||
|
// Produce a null pointer if 0 or invalid size is requested
|
||||||
|
return Address.fromInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocation must be large enough to hold the two list pointers when the chunk becomes free again
|
||||||
|
if (sizeBytes < MIN_ALLOC_SIZE) {
|
||||||
|
sizeBytes = MIN_ALLOC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure all allocations are at least a multiple of 4 to maintain alignment
|
||||||
|
sizeBytes = (sizeBytes + 3) & 0xFFFFFFFC;
|
||||||
|
|
||||||
|
// always between 0-63
|
||||||
|
int bucket = getListBucket(sizeBytes);
|
||||||
|
|
||||||
|
if (bucket == 63) {
|
||||||
|
// special bucket for the huge allocations
|
||||||
|
// uses a different slower function
|
||||||
|
return laxHugeAlloc(sizeBytes, cleared);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load bitmask of buckets with free chunks
|
||||||
|
long bucketMask = addrHeap(ADDR_HEAP_BUCKETS_FREE_MASK).getLong();
|
||||||
|
|
||||||
|
// mask away the buckets that we know are too small for this allocation
|
||||||
|
bucketMask &= 0xFFFFFFFFFFFFFFFFL << bucket;
|
||||||
|
|
||||||
|
// there are no more buckets with free chunks
|
||||||
|
// need to sbrk
|
||||||
|
if (bucketMask == 0L) {
|
||||||
|
int sizePlusInts = sizeBytes + 8; // size + 2 ints
|
||||||
|
Address newChunk = growLastChunk(sizePlusInts);
|
||||||
|
|
||||||
|
// Out of memory
|
||||||
|
if (newChunk.toInt() == 0) {
|
||||||
|
return Address.fromInt(0); //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// provision the new chunk
|
||||||
|
newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
|
||||||
|
newChunk.add(sizeBytes + 4).putInt(sizePlusInts); // size integer at the end
|
||||||
|
|
||||||
|
// return the chunk, +4 bytes to skip size int
|
||||||
|
// we don't need to clear it because its new memory
|
||||||
|
return newChunk.add(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// at least one bucket exists containing a free chunk,
|
||||||
|
// quickly determine which bucket it is with bit hacks
|
||||||
|
int availableBucket = Long.numberOfTrailingZeros(bucketMask);
|
||||||
|
|
||||||
|
Address bucketStartAddr = addrHeap(ADDR_HEAP_BUCKETS_START).add(availableBucket << SIZEOF_PTR_SH);
|
||||||
|
Address chunkPtr = bucketStartAddr.getAddress();
|
||||||
|
int chunkSize = readChunkSizeStatus(chunkPtr);
|
||||||
|
Address itrChunkStart = Address.fromInt(0);
|
||||||
|
|
||||||
|
// check if the first chunk in the bucket is large enough
|
||||||
|
if (chunkSize - 8 < sizeBytes) { // size - 2 ints
|
||||||
|
|
||||||
|
// the chunk is not large enough, move the first chunk to the end of the list
|
||||||
|
// and then check in the next bucket (where the chunks are definitely large enough)
|
||||||
|
// this functionality is present in emmalloc (emscripten)
|
||||||
|
|
||||||
|
Address chunkNextPtr = readChunkNextFreeAddr(chunkPtr);
|
||||||
|
if (chunkNextPtr.getInt() != chunkPtr.getInt()) {
|
||||||
|
bucketStartAddr.putAddress(chunkNextPtr);
|
||||||
|
itrChunkStart = chunkNextPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extend mask to the next bucket
|
||||||
|
bucketMask &= 0xFFFFFFFFFFFFFFFFL << (bucket + 1);
|
||||||
|
|
||||||
|
if (bucketMask != 0L) {
|
||||||
|
// there is a bucket with a larger chunk
|
||||||
|
int availableLargerBucket = Long.numberOfTrailingZeros(bucketMask);
|
||||||
|
Address largerBucketStartAddr = addrHeap(ADDR_HEAP_BUCKETS_START)
|
||||||
|
.add(availableLargerBucket << SIZEOF_PTR_SH);
|
||||||
|
Address largerChunkPtr = largerBucketStartAddr.getAddress();
|
||||||
|
int largerChunkSize = readChunkSizeStatus(largerChunkPtr);
|
||||||
|
|
||||||
|
// this will remove the chunk from the free list
|
||||||
|
allocateMemoryFromChunk(largerChunkPtr, largerChunkSize, sizeBytes);
|
||||||
|
|
||||||
|
// +4 bytes to skip size int
|
||||||
|
Address ret = largerChunkPtr.add(4);
|
||||||
|
|
||||||
|
// clear if requested
|
||||||
|
if (cleared) {
|
||||||
|
DirectMalloc.zmemset(ret, sizeBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the first chunk in the bucket is large enough
|
||||||
|
// this will remove the chunk from the free list
|
||||||
|
allocateMemoryFromChunk(chunkPtr, chunkSize, sizeBytes);
|
||||||
|
|
||||||
|
// +4 bytes to skip size int
|
||||||
|
Address ret = chunkPtr.add(4);
|
||||||
|
|
||||||
|
// clear if requested
|
||||||
|
if (cleared) {
|
||||||
|
DirectMalloc.zmemset(ret, sizeBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itrChunkStart.toInt() != 0) {
|
||||||
|
|
||||||
|
// if we've reached this point, it means the first chunk in the bucket wasn't large enough
|
||||||
|
// and there weren't any chunks in the larger buckets we could split up
|
||||||
|
// so we need to look closer
|
||||||
|
|
||||||
|
// iterate the (only) bucket of possibly large enough chunks
|
||||||
|
Address addrIterator = itrChunkStart;
|
||||||
|
do {
|
||||||
|
chunkSize = readChunkSizeStatus(addrIterator);
|
||||||
|
|
||||||
|
// check if the chunk is large enough
|
||||||
|
if (chunkSize - 8 >= sizeBytes) { // size - 2 ints
|
||||||
|
// we've found a large enough chunk
|
||||||
|
// this will remove the chunk from the free list
|
||||||
|
allocateMemoryFromChunk(addrIterator, chunkSize, sizeBytes);
|
||||||
|
|
||||||
|
// +4 bytes to skip size int
|
||||||
|
Address ret = addrIterator.add(4);
|
||||||
|
|
||||||
|
// clear if requested
|
||||||
|
if (cleared) {
|
||||||
|
DirectMalloc.zmemset(ret, sizeBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
addrIterator = readChunkNextFreeAddr(addrIterator);
|
||||||
|
} while (addrIterator.getInt() != chunkPtr.getInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// no other options, time to sbrk
|
||||||
|
|
||||||
|
int sizePlusInts = sizeBytes + 8; // size + 2 ints
|
||||||
|
Address newChunk = growLastChunk(sizePlusInts);
|
||||||
|
|
||||||
|
// Out of memory
|
||||||
|
if (newChunk.toInt() == 0) {
|
||||||
|
return Address.fromInt(0); //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// provision the new chunk
|
||||||
|
newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
|
||||||
|
newChunk.add(sizeBytes + 4).putInt(sizePlusInts); // size integer at the end
|
||||||
|
|
||||||
|
// return the chunk, +4 bytes to skip size int
|
||||||
|
// we don't need to clear it because its new memory
|
||||||
|
return newChunk.add(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Address laxHugeAlloc(int sizeBytes, boolean cleared) {
|
||||||
|
|
||||||
|
// check the bucket mask if bucket 63 has any chunks
|
||||||
|
if ((addrHeap(ADDR_HEAP_BUCKETS_FREE_MASK).getLong() & 0x8000000000000000L) != 0) {
|
||||||
|
|
||||||
|
// bucket 63 address
|
||||||
|
Address bucketStartAddr = addrHeap(ADDR_HEAP_BUCKETS_START).add(63 << SIZEOF_PTR_SH);
|
||||||
|
Address chunkPtr = bucketStartAddr.getAddress();
|
||||||
|
|
||||||
|
// iterate all free huge chunks
|
||||||
|
Address addrIterator = chunkPtr;
|
||||||
|
do {
|
||||||
|
int chunkSize = readChunkSizeStatus(addrIterator);
|
||||||
|
|
||||||
|
if (chunkSize - 8 >= sizeBytes) { // size - 2 ints
|
||||||
|
// we've found a large enough chunk
|
||||||
|
// this will remove the chunk from the free list
|
||||||
|
allocateMemoryFromChunk(addrIterator, chunkSize, sizeBytes);
|
||||||
|
|
||||||
|
// +4 bytes to skip size int
|
||||||
|
Address ret = addrIterator.add(4);
|
||||||
|
|
||||||
|
// clear if requested
|
||||||
|
if (cleared) {
|
||||||
|
DirectMalloc.zmemset(ret, sizeBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
addrIterator = readChunkNextFreeAddr(addrIterator);
|
||||||
|
} while (addrIterator.getInt() != chunkPtr.getInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// no free huge chunks found, time to sbrk
|
||||||
|
|
||||||
|
int sizePlusInts = sizeBytes + 8; // size + 2 ints
|
||||||
|
Address newChunk = growLastChunk(sizePlusInts);
|
||||||
|
|
||||||
|
// Out of memory
|
||||||
|
if (newChunk.toInt() == 0) {
|
||||||
|
return Address.fromInt(0); //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// provision the new chunk
|
||||||
|
newChunk.putInt(sizePlusInts | 0x80000000); // size + in use flag
|
||||||
|
newChunk.add(sizeBytes + 4).putInt(sizePlusInts); // size integer at the end
|
||||||
|
|
||||||
|
// return the chunk, +4 bytes to skip size int
|
||||||
|
// we don't need to clear it because its new memory
|
||||||
|
return newChunk.add(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* free implementation<br><br>
|
||||||
|
*
|
||||||
|
* bad things will happen if you free an address that was never allocated
|
||||||
|
*/
|
||||||
|
public static void laxFree(Address address) {
|
||||||
|
if (address.toInt() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// chunk actually starts 4 bytes before
|
||||||
|
Address chunkPtr = address.add(-4);
|
||||||
|
|
||||||
|
// bring the size of the chunk into the stack
|
||||||
|
int chunkSize = chunkPtr.getInt();
|
||||||
|
boolean sizeChanged = false;
|
||||||
|
|
||||||
|
// set the chunk no longer in use
|
||||||
|
chunkSize &= 0x7FFFFFFF;
|
||||||
|
|
||||||
|
if (addrHeap(ADDR_HEAP_DATA_START).isLessThan(chunkPtr)) {
|
||||||
|
Address prevChunkPtr = chunkPtr.add(-(chunkPtr.add(-4).getInt()));
|
||||||
|
if (!prevChunkPtr.isLessThan(addrHeap(ADDR_HEAP_DATA_START))) {
|
||||||
|
// check if we can merge with the previous chunk, and move it to another bucket
|
||||||
|
int prevChunkSize = readChunkSizeStatus(prevChunkPtr);
|
||||||
|
if ((prevChunkSize & 0x80000000) == 0) {
|
||||||
|
// previous chunk is not in use, merge!
|
||||||
|
|
||||||
|
// remove the previous chunk from its list
|
||||||
|
unlinkChunkFromFreeList(prevChunkPtr, prevChunkSize);
|
||||||
|
|
||||||
|
// resize the current chunk to also contain the previous chunk
|
||||||
|
chunkPtr = prevChunkPtr;
|
||||||
|
chunkSize += prevChunkSize;
|
||||||
|
sizeChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Address nextChunkPtr = chunkPtr.add(chunkSize);
|
||||||
|
if (nextChunkPtr.isLessThan(addrHeap(ADDR_HEAP_INNER_LIMIT).getAddress())) {
|
||||||
|
// check if we can merge with the next chunk as well
|
||||||
|
int nextChunkSize = readChunkSizeStatus(nextChunkPtr);
|
||||||
|
if ((nextChunkSize & 0x80000000) == 0) {
|
||||||
|
// next chunk is not in use, merge!
|
||||||
|
|
||||||
|
// remove the next chunk from its list
|
||||||
|
unlinkChunkFromFreeList(nextChunkPtr, nextChunkSize);
|
||||||
|
|
||||||
|
// resize the current chunk to also contain the next chunk
|
||||||
|
chunkSize += nextChunkSize;
|
||||||
|
sizeChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the final chunk size (also clears the in use flag)
|
||||||
|
chunkPtr.putInt(chunkSize);
|
||||||
|
|
||||||
|
if (sizeChanged) {
|
||||||
|
// if the size of the chunk changed, we also need to update the chunk's second size integer
|
||||||
|
chunkPtr.add(chunkSize - 4).putInt(chunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the final chunk to the free chunks list
|
||||||
|
linkChunkInFreeList(chunkPtr, chunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates memory from a free chunk, if the allocSize is smaller than the chunkSize by
|
||||||
|
* enough of a margin then the chunk is split into two smaller chunks, and the upper part
|
||||||
|
* of the chunk is returned to a bucket of free chunks
|
||||||
|
*/
|
||||||
|
private static void allocateMemoryFromChunk(Address chunkPtr, int chunkSize, int allocSize) {
|
||||||
|
// remove the chunk from its bucket
|
||||||
|
unlinkChunkFromFreeList(chunkPtr, chunkSize);
|
||||||
|
|
||||||
|
int otherHalfSize = chunkSize - allocSize - 8; // -size - 2 ints
|
||||||
|
|
||||||
|
// check if we can split the chunk into two smaller chunks
|
||||||
|
// chunk must be large enough to hold the 2 list pointers
|
||||||
|
if (otherHalfSize - (2 << SIZEOF_PTR_SH) >= MIN_ALLOC_SIZE) {
|
||||||
|
// chunk is large enough to split
|
||||||
|
|
||||||
|
// provision the lower part of the chunk, the part we want to use
|
||||||
|
int sizePlusInts = allocSize + 8; // size + 2 ints
|
||||||
|
chunkPtr.putInt(sizePlusInts | 0x80000000); // size + in use flag
|
||||||
|
chunkPtr.add(allocSize + 4).putInt(sizePlusInts); // size integer at the end
|
||||||
|
|
||||||
|
// provision the upper part of the chunk that we want to return to the free list
|
||||||
|
Address otherChunkPtr = chunkPtr.add(sizePlusInts);
|
||||||
|
otherChunkPtr.putInt(otherHalfSize); // size
|
||||||
|
otherChunkPtr.add(otherHalfSize - 4).putInt(otherHalfSize); // size (end)
|
||||||
|
|
||||||
|
// return the upper part of the chunk to the free chunks list
|
||||||
|
linkChunkInFreeList(otherChunkPtr, otherHalfSize);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// not large enough to split, just take the entire chunk
|
||||||
|
chunkPtr.putInt(chunkSize | 0x80000000); // sets the in use flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a free chunk to its corresponding bucket
|
||||||
|
*/
|
||||||
|
private static void linkChunkInFreeList(Address chunkPtr, int chunkSize) {
|
||||||
|
int bucket = getListBucket(chunkSize - 8); // size - 2 ints
|
||||||
|
|
||||||
|
long bucketMask = addrHeap(ADDR_HEAP_BUCKETS_FREE_MASK).getLong();
|
||||||
|
Address bucketStartAddr = addrHeap(ADDR_HEAP_BUCKETS_START).add(bucket << SIZEOF_PTR_SH);
|
||||||
|
|
||||||
|
// test the bucket mask if the bucket is empty
|
||||||
|
if ((bucketMask & (1L << bucket)) == 0L) {
|
||||||
|
|
||||||
|
// bucket is empty, add the free chunk to the list
|
||||||
|
bucketStartAddr.putAddress(chunkPtr);
|
||||||
|
writeChunkPrevFreeAddr(chunkPtr, chunkPtr);
|
||||||
|
writeChunkNextFreeAddr(chunkPtr, chunkPtr);
|
||||||
|
|
||||||
|
// set the free bit in bucket mask
|
||||||
|
bucketMask |= 1L << bucket;
|
||||||
|
addrHeap(ADDR_HEAP_BUCKETS_FREE_MASK).putLong(bucketMask);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// bucket is not empty, append to the bucket's existing free chunks list
|
||||||
|
Address otherBucketStart = bucketStartAddr.getAddress();
|
||||||
|
Address otherBucketPrev = readChunkPrevFreeAddr(otherBucketStart);
|
||||||
|
|
||||||
|
// link new chunk to the existing chunks in the bucket
|
||||||
|
writeChunkPrevFreeAddr(chunkPtr, otherBucketPrev);
|
||||||
|
writeChunkNextFreeAddr(chunkPtr, otherBucketStart);
|
||||||
|
|
||||||
|
// link the existing chunks in the bucket to the new chunk
|
||||||
|
writeChunkPrevFreeAddr(otherBucketStart, chunkPtr);
|
||||||
|
writeChunkNextFreeAddr(otherBucketPrev, chunkPtr);
|
||||||
|
|
||||||
|
// put the chunk in the bucket
|
||||||
|
bucketStartAddr.putAddress(chunkPtr);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a free chunk from its corresponding bucket
|
||||||
|
*/
|
||||||
|
private static void unlinkChunkFromFreeList(Address chunkPtr, int chunkSize) {
|
||||||
|
Address prevChunkPtr = readChunkPrevFreeAddr(chunkPtr);
|
||||||
|
Address nextChunkPtr = readChunkNextFreeAddr(chunkPtr);
|
||||||
|
if (prevChunkPtr.toInt() == chunkPtr.toInt() && nextChunkPtr.toInt() == chunkPtr.toInt()) {
|
||||||
|
// chunk is the only one currently in its bucket
|
||||||
|
|
||||||
|
int chunkBucket = getListBucket(chunkSize - 8); // size - 2 ints
|
||||||
|
|
||||||
|
Address bucketStartAddr = addrHeap(ADDR_HEAP_BUCKETS_START).add(chunkBucket << SIZEOF_PTR_SH);
|
||||||
|
bucketStartAddr.putAddress(Address.fromInt(0)); // remove chunk from the bucket
|
||||||
|
|
||||||
|
// clear the bit in the free buckets bitmask
|
||||||
|
long bucketsFreeMask = addrHeap(ADDR_HEAP_BUCKETS_FREE_MASK).getLong();
|
||||||
|
bucketsFreeMask &= ~(1L << chunkBucket);
|
||||||
|
addrHeap(ADDR_HEAP_BUCKETS_FREE_MASK).putLong(bucketsFreeMask);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// there are other chunks in this bucket
|
||||||
|
|
||||||
|
// link the next chunk to the previous chunk
|
||||||
|
writeChunkNextFreeAddr(prevChunkPtr, nextChunkPtr);
|
||||||
|
writeChunkPrevFreeAddr(nextChunkPtr, prevChunkPtr);
|
||||||
|
|
||||||
|
int chunkBucket = getListBucket(chunkSize - 8); // size - 2 ints
|
||||||
|
Address bucketStartAddr = addrHeap(ADDR_HEAP_BUCKETS_START).add(chunkBucket << SIZEOF_PTR_SH);
|
||||||
|
Address bucketStartChunk = bucketStartAddr.getAddress();
|
||||||
|
|
||||||
|
// chunk is the first in the bucket, so we also need to
|
||||||
|
// update the bucket to point to the next chunk instead
|
||||||
|
if (bucketStartChunk.toInt() == chunkPtr.toInt()) {
|
||||||
|
bucketStartAddr.putAddress(nextChunkPtr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://github.com/emscripten-core/emscripten/blob/16a0bf174cb85f88b6d9dcc8ee7fbca59390185b/system/
|
||||||
|
* lib/emmalloc.c#L241
|
||||||
|
* (MIT License)
|
||||||
|
*/
|
||||||
|
private static int getListBucket(int allocSize) {
|
||||||
|
if (allocSize < 128) {
|
||||||
|
return (allocSize >> 3) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int clz = Integer.numberOfLeadingZeros(allocSize);
|
||||||
|
int bucketIndex = (clz > 19) ? 110 - (clz << 2) + ((allocSize >> (29 - clz)) ^ 4)
|
||||||
|
: min(71 - (clz << 1) + ((allocSize >> (30 - clz)) ^ 2), 63);
|
||||||
|
|
||||||
|
return bucketIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the last chunk from the heap (if free), then grows the heap by amount minus
|
||||||
|
* the length of the last chunk, this shouldn't be called unless the program has failed
|
||||||
|
* to find a free chunk that is larger than the requested amount
|
||||||
|
*/
|
||||||
|
private static Address growLastChunk(int amount) {
|
||||||
|
Address lastAddr = addrHeap(ADDR_HEAP_INNER_LIMIT).getAddress();
|
||||||
|
|
||||||
|
// make sure it doesn't crash if the heap is empty
|
||||||
|
if (!addrHeap(ADDR_HEAP_DATA_START).isLessThan(lastAddr)) {
|
||||||
|
return growHeap(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the length and address of the last chunk
|
||||||
|
int lastLen = lastAddr.add(-4).getInt();
|
||||||
|
Address lastChunk = lastAddr.add(-lastLen);
|
||||||
|
lastLen = lastChunk.getInt();
|
||||||
|
|
||||||
|
// check if the last chunk is free
|
||||||
|
if ((lastLen & 0x80000000) == 0) {
|
||||||
|
|
||||||
|
// chunk is free, attempt to resize the heap first
|
||||||
|
// so errors can be handled
|
||||||
|
if (growHeap(amount - lastLen).toInt() == 0) {
|
||||||
|
// out of memory
|
||||||
|
return Address.fromInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlink last chunk from free list
|
||||||
|
unlinkChunkFromFreeList(lastChunk, lastLen);
|
||||||
|
|
||||||
|
// return the start of the last chunk
|
||||||
|
return lastChunk;
|
||||||
|
} else {
|
||||||
|
// no free chunk at the end of the heap
|
||||||
|
// just grow the heap by the full amount
|
||||||
|
return growHeap(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is our sbrk
|
||||||
|
*/
|
||||||
|
private static Address growHeap(int amount) {
|
||||||
|
Address heapInnerLimit = addrHeap(ADDR_HEAP_INNER_LIMIT).getAddress();
|
||||||
|
Address heapOuterLimit = addrHeap(ADDR_HEAP_OUTER_LIMIT).getAddress();
|
||||||
|
Address newHeapInnerLimit = heapInnerLimit.add(amount);
|
||||||
|
if (heapOuterLimit.isLessThan(newHeapInnerLimit)) {
|
||||||
|
int bytesNeeded = newHeapInnerLimit.toInt() - heapOuterLimit.toInt();
|
||||||
|
bytesNeeded = (bytesNeeded + 0xFFFF) & 0xFFFF0000;
|
||||||
|
Address newHeapOuterLimit = heapOuterLimit.add(bytesNeeded);
|
||||||
|
if (!getHeapMaxAddr().isLessThan(newHeapOuterLimit) && growHeapOuter(bytesNeeded >>> 16) != -1) {
|
||||||
|
addrHeap(ADDR_HEAP_INNER_LIMIT).putAddress(newHeapInnerLimit);
|
||||||
|
addrHeap(ADDR_HEAP_OUTER_LIMIT).putAddress(newHeapOuterLimit);
|
||||||
|
notifyHeapResized();
|
||||||
|
return heapInnerLimit;
|
||||||
|
} else {
|
||||||
|
return Address.fromInt(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addrHeap(ADDR_HEAP_INNER_LIMIT).putAddress(newHeapInnerLimit);
|
||||||
|
return heapInnerLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that on a free chunk, this is the size, because the status bit is 0
|
||||||
|
*/
|
||||||
|
private static int readChunkSizeStatus(Address chunkAddr) {
|
||||||
|
return chunkAddr.getInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readChunkSize(Address chunkAddr) {
|
||||||
|
return chunkAddr.getInt() & 0x7FFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean readChunkInUse(Address chunkAddr) {
|
||||||
|
return (chunkAddr.getInt() & 0x80000000) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeChunkSizeStatus(Address chunkAddr, int sizeStatus) {
|
||||||
|
chunkAddr.putInt(sizeStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Address readChunkPrevFreeAddr(Address chunkAddr) {
|
||||||
|
return chunkAddr.add(4).getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeChunkPrevFreeAddr(Address chunkAddr, Address prevFree) {
|
||||||
|
chunkAddr.add(4).putAddress(prevFree);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Address readChunkNextFreeAddr(Address chunkAddr) {
|
||||||
|
return chunkAddr.add(4 + (1 << SIZEOF_PTR_SH)).getAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeChunkNextFreeAddr(Address chunkAddr, Address nextFree) {
|
||||||
|
chunkAddr.add(4 + (1 << SIZEOF_PTR_SH)).putAddress(nextFree);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int min(int a, int b) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Import(name = "dumpHeapHelper")
|
||||||
|
// private static native void dumpHeapHelper(Address chunkStart, Address chunkEnd, int size,
|
||||||
|
// int free, Address endAddr);
|
||||||
|
//
|
||||||
|
// public static void heapDump() {
|
||||||
|
// Address curAddr = addrHeap(ADDR_HEAP_DATA_START);
|
||||||
|
// Address endAddr = addrHeap(ADDR_HEAP_INNER_LIMIT).getAddress();
|
||||||
|
// while (curAddr.isLessThan(endAddr)) {
|
||||||
|
// int sizeStat = readChunkSizeStatus(curAddr);
|
||||||
|
// int size = sizeStat & 0x7FFFFFFF;
|
||||||
|
// int stat = sizeStat >>> 31;
|
||||||
|
// dumpHeapHelper(curAddr, curAddr.add(size), size, stat, endAddr);
|
||||||
|
// if (size == 0) {
|
||||||
|
// //NOTE: size 0 would be a bug
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// curAddr = curAddr.add(size);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 lax1dude.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.interop;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linear memory allocator for creating "direct buffers" in WASM GC<br><br>
|
||||||
|
*
|
||||||
|
* DO NOT USE IN LEGACY WASM BACKEND!!! Make a regular byte array, and use Address.ofData()<br><br>
|
||||||
|
*
|
||||||
|
* Similar to dlmalloc and emmalloc (emscripten's malloc)<br><br>
|
||||||
|
*
|
||||||
|
* bad things will happen if you free an address that was never allocated
|
||||||
|
*
|
||||||
|
* @author lax1dude
|
||||||
|
*/
|
||||||
|
public final class DirectMalloc {
|
||||||
|
|
||||||
|
private DirectMalloc() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@UnsupportedOn({Platforms.JAVASCRIPT, Platforms.WEBASSEMBLY, Platforms.C})
|
||||||
|
public static native Address malloc(int sizeBytes);
|
||||||
|
|
||||||
|
@UnsupportedOn({Platforms.JAVASCRIPT, Platforms.WEBASSEMBLY, Platforms.C})
|
||||||
|
public static native Address calloc(int sizeBytes);
|
||||||
|
|
||||||
|
@UnsupportedOn({Platforms.JAVASCRIPT, Platforms.WEBASSEMBLY, Platforms.C})
|
||||||
|
public static native void free(Address ptr);
|
||||||
|
|
||||||
|
@UnsupportedOn({Platforms.JAVASCRIPT, Platforms.WEBASSEMBLY, Platforms.C})
|
||||||
|
public static native void memcpy(Address dst, Address src, int count);
|
||||||
|
|
||||||
|
@UnsupportedOn({Platforms.JAVASCRIPT, Platforms.WEBASSEMBLY, Platforms.C})
|
||||||
|
public static native void memset(Address ptr, int val, int count);
|
||||||
|
|
||||||
|
@UnsupportedOn({Platforms.JAVASCRIPT, Platforms.WEBASSEMBLY, Platforms.C})
|
||||||
|
public static native void zmemset(Address ptr, int count);
|
||||||
|
|
||||||
|
}
|
|
@ -119,6 +119,7 @@ public class TeaVMTool {
|
||||||
private Set<File> generatedFiles = new HashSet<>();
|
private Set<File> generatedFiles = new HashSet<>();
|
||||||
private int minHeapSize = 4 * (1 << 20);
|
private int minHeapSize = 4 * (1 << 20);
|
||||||
private int maxHeapSize = 128 * (1 << 20);
|
private int maxHeapSize = 128 * (1 << 20);
|
||||||
|
private boolean directMallocSupport;
|
||||||
private ReferenceCache referenceCache;
|
private ReferenceCache referenceCache;
|
||||||
private boolean heapDump;
|
private boolean heapDump;
|
||||||
private boolean shortFileNames;
|
private boolean shortFileNames;
|
||||||
|
@ -268,6 +269,10 @@ public class TeaVMTool {
|
||||||
this.maxHeapSize = maxHeapSize;
|
this.maxHeapSize = maxHeapSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDirectMallocSupport(boolean enableDirectMalloc) {
|
||||||
|
this.directMallocSupport = enableDirectMalloc;
|
||||||
|
}
|
||||||
|
|
||||||
public ClassLoader getClassLoader() {
|
public ClassLoader getClassLoader() {
|
||||||
return classLoader;
|
return classLoader;
|
||||||
}
|
}
|
||||||
|
@ -411,6 +416,11 @@ public class TeaVMTool {
|
||||||
target.setSourceMapBuilder(wasmSourceMapWriter);
|
target.setSourceMapBuilder(wasmSourceMapWriter);
|
||||||
target.setSourceMapLocation(getResolvedTargetFileName() + ".map");
|
target.setSourceMapLocation(getResolvedTargetFileName() + ".map");
|
||||||
}
|
}
|
||||||
|
if (directMallocSupport) {
|
||||||
|
target.setEnableDirectMallocSupport(directMallocSupport);
|
||||||
|
target.setDirectMallocMinHeapSize(minHeapSize);
|
||||||
|
target.setDirectMallocMaxHeapSize(maxHeapSize);
|
||||||
|
}
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,8 @@ public interface BuildStrategy {
|
||||||
|
|
||||||
void setWasmDebugInfoLocation(WasmDebugInfoLocation wasmDebugInfoLocation);
|
void setWasmDebugInfoLocation(WasmDebugInfoLocation wasmDebugInfoLocation);
|
||||||
|
|
||||||
|
void setDirectMallocSupport(boolean enable);
|
||||||
|
|
||||||
void setMinHeapSize(int minHeapSize);
|
void setMinHeapSize(int minHeapSize);
|
||||||
|
|
||||||
void setMaxHeapSize(int maxHeapSize);
|
void setMaxHeapSize(int maxHeapSize);
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||||
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
private TeaVMToolLog log = new EmptyTeaVMToolLog();
|
||||||
private boolean shortFileNames;
|
private boolean shortFileNames;
|
||||||
private boolean assertionsRemoved;
|
private boolean assertionsRemoved;
|
||||||
|
private boolean directMallocSupport;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
|
@ -258,6 +259,11 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||||
this.assertionsRemoved = assertionsRemoved;
|
this.assertionsRemoved = assertionsRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDirectMallocSupport(boolean enable) {
|
||||||
|
this.directMallocSupport = enable;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BuildResult build() throws BuildException {
|
public BuildResult build() throws BuildException {
|
||||||
TeaVMTool tool = new TeaVMTool();
|
TeaVMTool tool = new TeaVMTool();
|
||||||
|
@ -289,6 +295,7 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||||
tool.setWasmExceptionsUsed(wasmExceptionsUsed);
|
tool.setWasmExceptionsUsed(wasmExceptionsUsed);
|
||||||
tool.setWasmDebugInfoLevel(wasmDebugInfoLevel);
|
tool.setWasmDebugInfoLevel(wasmDebugInfoLevel);
|
||||||
tool.setWasmDebugInfoLocation(wasmDebugInfoLocation);
|
tool.setWasmDebugInfoLocation(wasmDebugInfoLocation);
|
||||||
|
tool.setDirectMallocSupport(directMallocSupport);
|
||||||
tool.setMinHeapSize(minHeapSize);
|
tool.setMinHeapSize(minHeapSize);
|
||||||
tool.setMaxHeapSize(maxHeapSize);
|
tool.setMaxHeapSize(maxHeapSize);
|
||||||
tool.setHeapDump(heapDump);
|
tool.setHeapDump(heapDump);
|
||||||
|
|
|
@ -214,6 +214,11 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
||||||
request.maxHeapSize = maxHeapSize;
|
request.maxHeapSize = maxHeapSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDirectMallocSupport(boolean enable) {
|
||||||
|
request.directMallocSupport = enable;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setHeapDump(boolean heapDump) {
|
public void setHeapDump(boolean heapDump) {
|
||||||
request.heapDump = heapDump;
|
request.heapDump = heapDump;
|
||||||
|
|
|
@ -59,4 +59,5 @@ public class RemoteBuildRequest implements Serializable {
|
||||||
public boolean heapDump;
|
public boolean heapDump;
|
||||||
public boolean shortFileNames;
|
public boolean shortFileNames;
|
||||||
public boolean assertionsRemoved;
|
public boolean assertionsRemoved;
|
||||||
|
public boolean directMallocSupport;
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,9 @@ public class TeaVMPlugin implements Plugin<Project> {
|
||||||
task.getStrict().convention(wasmGC.getStrict());
|
task.getStrict().convention(wasmGC.getStrict());
|
||||||
task.getSourceMap().convention(wasmGC.getSourceMap());
|
task.getSourceMap().convention(wasmGC.getSourceMap());
|
||||||
task.getSourceFilePolicy().convention(wasmGC.getSourceFilePolicy());
|
task.getSourceFilePolicy().convention(wasmGC.getSourceFilePolicy());
|
||||||
|
task.getDirectMallocSupport().convention(wasmGC.getDirectMallocSupport());
|
||||||
|
task.getMinHeapSize().convention(wasmGC.getMinHeapSize());
|
||||||
|
task.getMaxHeapSize().convention(wasmGC.getMaxHeapSize());
|
||||||
setupSources(task.getSourceFiles(), project);
|
setupSources(task.getSourceFiles(), project);
|
||||||
buildTask.dependsOn(task);
|
buildTask.dependsOn(task);
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,8 @@ package org.teavm.gradle.api;
|
||||||
|
|
||||||
import org.gradle.api.provider.Property;
|
import org.gradle.api.provider.Property;
|
||||||
|
|
||||||
public interface TeaVMWasmGCConfiguration extends TeaVMCommonConfiguration, TeaVMWebConfiguration {
|
public interface TeaVMWasmGCConfiguration extends TeaVMCommonConfiguration, TeaVMWebConfiguration,
|
||||||
|
TeaVMNativeBaseConfiguration {
|
||||||
Property<Boolean> getObfuscated();
|
Property<Boolean> getObfuscated();
|
||||||
|
|
||||||
Property<Boolean> getStrict();
|
Property<Boolean> getStrict();
|
||||||
|
@ -37,4 +38,6 @@ public interface TeaVMWasmGCConfiguration extends TeaVMCommonConfiguration, TeaV
|
||||||
Property<SourceFilePolicy> getSourceFilePolicy();
|
Property<SourceFilePolicy> getSourceFilePolicy();
|
||||||
|
|
||||||
Property<Boolean> getModularRuntime();
|
Property<Boolean> getModularRuntime();
|
||||||
|
|
||||||
|
Property<Boolean> getDirectMallocSupport();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.teavm.tooling.TeaVMTargetType;
|
||||||
import org.teavm.tooling.builder.BuildStrategy;
|
import org.teavm.tooling.builder.BuildStrategy;
|
||||||
|
|
||||||
public abstract class GenerateWasmGCTask extends TeaVMTask {
|
public abstract class GenerateWasmGCTask extends TeaVMTask {
|
||||||
|
private static final int MB = 1024 * 1024;
|
||||||
|
|
||||||
public GenerateWasmGCTask() {
|
public GenerateWasmGCTask() {
|
||||||
getStrict().convention(true);
|
getStrict().convention(true);
|
||||||
getObfuscated().convention(true);
|
getObfuscated().convention(true);
|
||||||
|
@ -34,6 +36,9 @@ public abstract class GenerateWasmGCTask extends TeaVMTask {
|
||||||
getDebugInfoLocation().convention(WasmDebugInfoLocation.EXTERNAL);
|
getDebugInfoLocation().convention(WasmDebugInfoLocation.EXTERNAL);
|
||||||
getSourceMap().convention(false);
|
getSourceMap().convention(false);
|
||||||
getSourceFilePolicy().convention(SourceFilePolicy.LINK_LOCAL_FILES);
|
getSourceFilePolicy().convention(SourceFilePolicy.LINK_LOCAL_FILES);
|
||||||
|
getDirectMallocSupport().convention(false);
|
||||||
|
getMinHeapSize().convention(1);
|
||||||
|
getMaxHeapSize().convention(16);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
|
@ -58,6 +63,18 @@ public abstract class GenerateWasmGCTask extends TeaVMTask {
|
||||||
@Optional
|
@Optional
|
||||||
public abstract Property<SourceFilePolicy> getSourceFilePolicy();
|
public abstract Property<SourceFilePolicy> getSourceFilePolicy();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Boolean> getDirectMallocSupport();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Integer> getMinHeapSize();
|
||||||
|
|
||||||
|
@Input
|
||||||
|
@Optional
|
||||||
|
public abstract Property<Integer> getMaxHeapSize();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupBuilder(BuildStrategy builder) {
|
protected void setupBuilder(BuildStrategy builder) {
|
||||||
builder.setStrict(getStrict().get());
|
builder.setStrict(getStrict().get());
|
||||||
|
@ -83,5 +100,8 @@ public abstract class GenerateWasmGCTask extends TeaVMTask {
|
||||||
builder.setTargetType(TeaVMTargetType.WEBASSEMBLY_GC);
|
builder.setTargetType(TeaVMTargetType.WEBASSEMBLY_GC);
|
||||||
TaskUtils.applySourceFiles(getSourceFiles(), builder);
|
TaskUtils.applySourceFiles(getSourceFiles(), builder);
|
||||||
TaskUtils.applySourceFilePolicy(getSourceFilePolicy(), builder);
|
TaskUtils.applySourceFilePolicy(getSourceFilePolicy(), builder);
|
||||||
|
builder.setDirectMallocSupport(getDirectMallocSupport().getOrElse(false));
|
||||||
|
builder.setMinHeapSize(getMinHeapSize().get() * MB);
|
||||||
|
builder.setMaxHeapSize(getMaxHeapSize().get() * MB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user