Skip to content

Proposal to make runtime functionality configurable. #46

Description

@tenko

Right now the functions createNewCall, createFreeCall in src\codegen\llvm\LLVMIRBuilder.cpp has hard coded references to malloc, free, abort and exit. This can be made more flexible by utilizing the linker support for weak symbols.

I replaced malloc with OberonRuntime_Allocate:

Value *
LLVMIRBuilder::createNewCall(TypeNode *type, llvm::Value *param) {
    auto fun = module_->getFunction("OberonRuntime_Allocate");
    if (!fun) {
        auto funTy = FunctionType::get(builder_.getPtrTy(), { builder_.getInt64Ty() }, false);
        fun = Function::Create(funTy, GlobalValue::ExternalLinkage, "OberonRuntime_Allocate", module_);
        fun->addFnAttr(Attribute::getWithAllocSizeArgs(builder_.getContext(), 0, {}));
        fun->addParamAttr(0, Attribute::NoUndef);
    }
    ...

and free with OberonRuntime_Deallocate:

Value *
LLVMIRBuilder::createFreeCall([[maybe_unused]] TypeNode *type, Value *param) {
    auto fun = module_->getFunction("OberonRuntime_Deallocate");
#ifdef _LLVM_LEGACY
    auto voidTy = PointerType::get(builder_.getVoidTy(), 0);
#else
    auto voidTy = builder_.getPtrTy();
#endif
    if (!fun) {
        auto funTy = FunctionType::get(builder_.getVoidTy(), {voidTy}, false);
        fun = Function::Create(funTy, GlobalValue::ExternalLinkage, "OberonRuntime_Deallocate", module_);
        fun->addParamAttr(0, Attribute::NoUndef);
    }
    ...

Then creates a default implementation OberonRuntimeDefault.c:

#include <stdlib.h>
#include <stdio.h>

__attribute__((weak)) void *OberonRuntime_Allocate( size_t size ) {
    printf("OberonRuntimeDefault.Allocate\n");
    return malloc(size);
}

__attribute__((weak)) void OberonRuntime_Deallocate( void *ptr ) {
    printf("OberonRuntimeDefault.Deallocate\n");
    free(ptr);
}

Functions here are decorated with the weak attribute.
This is then just compiled to a static library and works as expected when linked.

This can then exploited to replace the functionality with for example a Garbage Collector (Boehm):

#include <stdlib.h>
#include <stdio.h>
#include <gc.h>

void *OberonRuntime_Allocate( size_t size ) {
    printf("OberonRuntimeGC.Allocate\n");
    return GC_malloc(size);
}

void OberonRuntime_Deallocate( void *ptr ) {
    printf("OberonRuntimeGC.Deallocate\n");
    // Skip
}

By linking this as a static library, these definitions take precedence over the weak version.
(This could also be done with an Oberon module.)

In order for this to work as expected with the JIT implementation, default functions need to be injected.
Perhaps default functions can just be defined in the main module.

Also this can be expanded to cover putchar etc., so that the Out module can be redirected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions