Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,34 @@ function build_modules()
if [ -d "$mod" ] ; then
echo Building module $mod
pushd $mod
if [ -f module.c ] ; then
mod_sources=""
if [ -f module_params.yml ] ; then
mod_sources="$(yq -r '(.options.sources // [])[] | .value' module_params.yml 2>/dev/null | xargs)"
fi
if [ -n "${mod_sources}" ] ; then
# Multi-file module (options.sources in
# module_params.yml): compile each source as a plain
# ELF object (no LTO - the sources carry their own
# flags from options.cc, e.g. rv64im for the
# soft-float builtins, and must not be re-codegenned
# at LTO link time with the common flags) and merge
# with ld -r.
mod_cflags="${cflags_common}"
yml_cflags="$(yq -r '(.options.cc // [])[] | .value' module_params.yml 2>/dev/null | xargs)"
if [ -n "${yml_cflags}" ] ; then
mod_cflags="--target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu ${yml_cflags}"
fi
rm -rf obj && mkdir -p obj
objs=""
for srcf in ${mod_sources} ; do
of="obj/$(echo "$srcf" | tr / _).o"
clang ${mod_cflags} -DBFLAT_DOTNET=${dotnet_version} -c "$srcf" -o "$of"
on_fail $? "Failed to compile module $mod source $srcf"
objs="$objs $of"
done
riscv64-linux-gnu-ld -r $objs -o module.o
on_fail $? "Failed to merge module $mod objects"
elif [ -f module.c ] ; then
# Compile module as C (clang + LTO so lld can do cross-module opt)
# -DBFLAT_DOTNET: some module bodies are contract-versioned
# against the target runtime (the C/C++ analog of the .S
Expand Down
177 changes: 166 additions & 11 deletions src/bflat/BuildCommand.cs

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions src/bflat/ExtLibResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,22 @@ private static Result ParseManifestAndResolveFiles(
if (staticLibRel != null)
{
string absPath = Path.Combine(manifestDir, staticLibRel.Replace('/', Path.DirectorySeparatorChar));
if (!File.Exists(absPath))
{
// NuGet layout: the manifest lives under contentFiles/any/any/
// while runtimes/<rid>/native/ sits at the package root, so a
// manifest-relative path misses it. Walk up towards the
// extraction root and take the first match.
for (string dir = manifestDir; dir != null; dir = Path.GetDirectoryName(dir))
{
string candidate = Path.Combine(dir, staticLibRel.Replace('/', Path.DirectorySeparatorChar));
if (File.Exists(candidate))
{
absPath = candidate;
break;
}
}
}
if (!File.Exists(absPath))
throw new Exception(
$"Static library not found: {absPath} (referenced from manifest '{manifestPath}')");
Expand Down
14 changes: 14 additions & 0 deletions src/bflat/InstructionSetHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -69,6 +70,19 @@ public static InstructionSetSupport ConfigureInstructionSetSupport(string instru
instructionSetSupportBuilder.AddSupportedInstructionSet("neon"); // Lower baselines included by implication
}
}
else if (targetArchitecture == TargetArchitecture.RiscV64
&& Environment.GetEnvironmentVariable("BFLAT_NO_RISCV_BASELINE") != "1")
{
// The rv64gc baseline: "d" implies "f" and "base"; "c" and "a"
// complete the G+C set. Reduced-ISA targets opt out with
// --targetisa negation (e.g. -f,-d) - an unsupported F extension
// switches ilc and the JIT to the lp64 soft-float ABI and
// soft-float lowering.
instructionSetSupportBuilder.AddSupportedInstructionSet("base");
instructionSetSupportBuilder.AddSupportedInstructionSet("d");
instructionSetSupportBuilder.AddSupportedInstructionSet("c");
instructionSetSupportBuilder.AddSupportedInstructionSet("a");
}

// Whether to allow optimistically expanding the instruction sets beyond what was specified.
// We seed this from optimizingForSize - if we're size-optimizing, we don't want to unnecessarily
Expand Down
2 changes: 1 addition & 1 deletion src/bflat/ZkvmIL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ public override MethodIL GetMethodIL(MethodDesc method)
// polynomial used only by float FORMATTING). RyuJIT materializes each float
// constant with flw/fld + fsw/fsd - this is the dominant FP in a .NET 11 image
// (~1536 instructions). The table's sole reader is Number.DiyFp128Sqrt, reachable
// only through Number.FormatFloat, which zisk.substitutions.xml removes - so the
// only through Number.FormatFloat, which zisk.nofp.substitutions.xml removes - so the
// table is dead. Keep the allocation (a zeroed SqrtCoefficients[256] - harmless
// even if ever indexed) but nop out the constant-filling body between the newarr
// and the stsfld: the array stays on the stack across the nops, so the store is
Expand Down
4 changes: 2 additions & 2 deletions src/bflat/ZkvmSubstitutions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ MethodDesc Snippet(string name)
// later (--error-on-float, or the emulator rejecting an F/D opcode).
// That already happened once - Number.FormatFloat's signature changed
// in .NET 11, the entry stopped matching, and floating point came back
// (see the note in zisk.substitutions.xml). So a missing target is
// (see the note in zisk.nofp.substitutions.xml). So a missing target is
// fatal, and the few genuinely conditional ones must say so.
void Add(MethodDesc target, MethodDesc snippet, string what)
{
Expand Down Expand Up @@ -268,7 +268,7 @@ ModuleDesc Module(string simpleName)

// System.Double / System.Single: the object overloads of Equals and
// CompareTo. They cannot be body="remove"d (see the note in
// zisk.substitutions.xml - they are what ValueType.Equals and the
// zisk.nofp.substitutions.xml - they are what ValueType.Equals and the
// generic comparers reach for), and the originals compare in FP
// registers, so they are the one FP source that survives into an
// ordinary guest. The snippets redo the same IEEE comparison on the raw
Expand Down
8 changes: 8 additions & 0 deletions src/bflat/bflat.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@
<Copy SourceFiles="$(MSBuildThisFileDirectory)modules\nofp\module_params.yml"
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\nofp.params.yml" />

<!-- SoftFloat (no-F targets: real soft-float builtins instead of nofp traps) -->
<Copy SourceFiles="$(MSBuildThisFileDirectory)modules\softfloat\module.o"
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\softfloat.o" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)modules\softfloat\module_params.yml"
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\softfloat.params.yml" />

<!-- PAL -->
<Copy SourceFiles="$(MSBuildThisFileDirectory)modules\pal\module.o"
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\pal.o" />
Expand Down Expand Up @@ -209,6 +215,8 @@
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\zisk_subst.params.yml" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)modules\zisk_subst\zisk.substitutions.xml"
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\zisk.substitutions.xml" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)modules\zisk_subst\zisk.nofp.substitutions.xml"
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\zisk.nofp.substitutions.xml" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)modules\zisk_subst\zisk.snippets.cs"
DestinationFiles="$(OutputPath)lib\linux\riscv64\zisk\zisk.snippets.cs" />

Expand Down
2 changes: 1 addition & 1 deletion src/bflat/bflat.variant.props
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

<RuntimeVersionPostfix Condition="'$(DotnetVersion)' == '10' and '$(Variant)' == 'perf'">.p3</RuntimeVersionPostfix>
<RuntimeVersionPostfix Condition="'$(DotnetVersion)' == '10' and '$(Variant)' == 'min'">.x10</RuntimeVersionPostfix>
<RuntimeVersionPostfix Condition="'$(DotnetVersion)' == '11' and '$(Variant)' == 'min'">.x9</RuntimeVersionPostfix>
<RuntimeVersionPostfix Condition="'$(DotnetVersion)' == '11' and '$(Variant)' == 'min'">.x13-sf</RuntimeVersionPostfix>

<!-- Full runtime release tag. RuntimeVersion stays the plain NuGet
version: every release ships bflat.compiler.$(RuntimeVersion).nupkg,
Expand Down
35 changes: 35 additions & 0 deletions src/bflat/modules/pal/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,41 @@ __wrap_mmap(void *addr, int length, int prot, int flags,
return __wrap___libc_malloc_impl(length);
}

/* The upstream GC (--zk-gc clr) commits its regions with mprotect(RW),
* advises with madvise(MADV_DONTDUMP/DONTNEED) and sizes the reservation by
* getrlimit(RLIMIT_AS). The guest has no MMU: every mmap is a bump
* allocation that is readable and writable from the start, so commit and
* advise are no-ops, and the address-space limit query fails (the GC then
* treats it as unlimited and falls back to the configured hard limit). musl's
* own implementations would issue raw ecalls the emulator does not serve. */

/*@ assigns \nothing;
ensures \result == 0;
*/
int
__wrap_mprotect(void *addr, int length, int prot)
{
return 0;
}

/*@ assigns \nothing;
ensures \result == 0;
*/
int
__wrap_madvise(void *addr, int length, int advice)
{
return 0;
}

/*@ assigns \nothing;
ensures \result == -1;
*/
int
__wrap_getrlimit(int resource, void *rlim)
{
return -1;
}

/*@ assigns \nothing;
ensures \result == 0;
*/
Expand Down
5 changes: 5 additions & 0 deletions src/bflat/modules/pal/module_params.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ options:
- value: --wrap=memalign
- value: --wrap=mmap
- value: --wrap=munmap
# No MMU: the upstream GC's commit/advise are no-ops, its address-space
# limit query fails (= unlimited); musl's versions would ecall.
- value: --wrap=mprotect
- value: --wrap=madvise
- value: --wrap=getrlimit
- value: --wrap=mlock
- value: --wrap=munlock
- value: --wrap=mlockall
Expand Down
1 change: 1 addition & 0 deletions src/bflat/modules/rhp/module_params.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ options:
- value: --wrap=RhpReversePInvokeReturn
# No write barrier under uGC: bulk reference copy is a plain memmove.
- value: --wrap=RhBulkMoveWithWriteBarrier
gc: ugc
# Event tracing is meaningless in the guest; candidates for replacement
# by the EventSourceSupport=false ILC feature switch.
- value: --wrap=S_P_CoreLib_System_Diagnostics_Tracing_EventPipeEventProvider__Register
Expand Down
7 changes: 6 additions & 1 deletion src/bflat/modules/rhp_native/module_params.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Linker options owned by the rhp_native module (packed as
# rhp_native.params.yml). uGC has no card table and never collects, so
# every GC write barrier reduces to the bare store; these are the hottest
# helpers in the image (hundreds of call sites).
# helpers in the image (hundreds of call sites). Only under uGC: the upstream
# GC (--zk-gc clr) needs the original card-marking barriers.
# RhpAssignRef (the non-RiscV64 name) has shown zero references in guest
# scans - kept as insurance until confirmed dead on a full workload.
options:
ld:
- value: --wrap=RhpAssignRefRiscV64
gc: ugc
- value: --wrap=RhpCheckedAssignRef
gc: ugc
- value: --wrap=RhpByRefAssignRef
gc: ugc
- value: --wrap=RhpAssignRef
gc: ugc
24 changes: 24 additions & 0 deletions src/bflat/modules/softfloat/builtins/adddf3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===-- lib/adddf3.c - Double-precision addition ------------------*- C -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements double-precision soft-float addition.
//
//===----------------------------------------------------------------------===//

#define DOUBLE_PRECISION
#include "fp_add_impl.inc"

COMPILER_RT_ABI double __adddf3(double a, double b) { return __addXf3__(a, b); }

#if defined(__ARM_EABI__)
#if defined(COMPILER_RT_ARMHF_TARGET)
AEABI_RTABI double __aeabi_dadd(double a, double b) { return __adddf3(a, b); }
#else
COMPILER_RT_ALIAS(__adddf3, __aeabi_dadd)
#endif
#endif
24 changes: 24 additions & 0 deletions src/bflat/modules/softfloat/builtins/addsf3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===-- lib/addsf3.c - Single-precision addition ------------------*- C -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements single-precision soft-float addition.
//
//===----------------------------------------------------------------------===//

#define SINGLE_PRECISION
#include "fp_add_impl.inc"

COMPILER_RT_ABI float __addsf3(float a, float b) { return __addXf3__(a, b); }

#if defined(__ARM_EABI__)
#if defined(COMPILER_RT_ARMHF_TARGET)
AEABI_RTABI float __aeabi_fadd(float a, float b) { return __addsf3(a, b); }
#else
COMPILER_RT_ALIAS(__addsf3, __aeabi_fadd)
#endif
#endif
77 changes: 77 additions & 0 deletions src/bflat/modules/softfloat/builtins/comparedf2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===-- lib/comparedf2.c - Double-precision comparisons -----------*- C -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// // This file implements the following soft-float comparison routines:
//
// __eqdf2 __gedf2 __unorddf2
// __ledf2 __gtdf2
// __ltdf2
// __nedf2
//
// The semantics of the routines grouped in each column are identical, so there
// is a single implementation for each, and wrappers to provide the other names.
//
// The main routines behave as follows:
//
// __ledf2(a,b) returns -1 if a < b
// 0 if a == b
// 1 if a > b
// 1 if either a or b is NaN
//
// __gedf2(a,b) returns -1 if a < b
// 0 if a == b
// 1 if a > b
// -1 if either a or b is NaN
//
// __unorddf2(a,b) returns 0 if both a and b are numbers
// 1 if either a or b is NaN
//
// Note that __ledf2( ) and __gedf2( ) are identical except in their handling of
// NaN values.
//
//===----------------------------------------------------------------------===//

#define DOUBLE_PRECISION
#include "fp_lib.h"

#include "fp_compare_impl.inc"

COMPILER_RT_ABI CMP_RESULT __ledf2(fp_t a, fp_t b) { return __leXf2__(a, b); }

#if defined(__ELF__)
// Alias for libgcc compatibility
COMPILER_RT_ALIAS(__ledf2, __cmpdf2)
#endif
COMPILER_RT_ALIAS(__ledf2, __eqdf2)
COMPILER_RT_ALIAS(__ledf2, __ltdf2)
COMPILER_RT_ALIAS(__ledf2, __nedf2)

COMPILER_RT_ABI CMP_RESULT __gedf2(fp_t a, fp_t b) { return __geXf2__(a, b); }

COMPILER_RT_ALIAS(__gedf2, __gtdf2)

COMPILER_RT_ABI CMP_RESULT __unorddf2(fp_t a, fp_t b) {
return __unordXf2__(a, b);
}

#if defined(__ARM_EABI__)
#if defined(COMPILER_RT_ARMHF_TARGET)
AEABI_RTABI int __aeabi_dcmpun(fp_t a, fp_t b) { return __unorddf2(a, b); }
#else
COMPILER_RT_ALIAS(__unorddf2, __aeabi_dcmpun)
#endif
#endif

#if defined(_WIN32) && !defined(__MINGW32__)
// The alias mechanism doesn't work on Windows except for MinGW, so emit
// wrapper functions.
int __eqdf2(fp_t a, fp_t b) { return __ledf2(a, b); }
int __ltdf2(fp_t a, fp_t b) { return __ledf2(a, b); }
int __nedf2(fp_t a, fp_t b) { return __ledf2(a, b); }
int __gtdf2(fp_t a, fp_t b) { return __gedf2(a, b); }
#endif
Loading
Loading