Skip to content
Merged
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
16 changes: 8 additions & 8 deletions Ubiquity.NET.ruleset
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@
<Rule Id="CA1715" Action="Error" />
<Rule Id="CA1717" Action="Warning" />
<Rule Id="CA1719" Action="Warning" />
<Rule Id="CA1720" Action="None" />
<Rule Id="CA1721" Action="Warning" />
<Rule Id="CA1722" Action="Warning" />
<Rule Id="CA1724" Action="Warning" />
Expand Down Expand Up @@ -580,10 +579,18 @@
<Rule Id="IDE0034" Action="Warning" />
<Rule Id="IDE0058" Action="None" />
<Rule Id="IDE0067" Action="None" />
<Rule Id="IDE0079" Action="Error" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.VisualBasic" RuleNamespace="Microsoft.CodeAnalysis.VisualBasic">
<Rule Id="AD0001" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1062" Action="Error" />
<Rule Id="CA1720" Action="None" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="Hidden" />
</Rules>
<Rules AnalyzerId="RefactoringEssentials" RuleNamespace="RefactoringEssentials">
<Rule Id="RECS0070" Action="None" />
<Rule Id="RECS0083" Action="Error" />
Expand Down Expand Up @@ -733,11 +740,4 @@
<Rule Id="SA1652" Action="Warning" />
<Rule Id="SX1101" Action="Error" />
</Rules>
<Rules AnalyzerId="Microsoft.CodeQuality.Analyzers" RuleNamespace="Microsoft.CodeQuality.Analyzers">
<Rule Id="CA1720" Action="None" />
<Rule Id="CA1062" Action="Error" />
</Rules>
<Rules AnalyzerId="Microsoft.NetCore.Analyzers" RuleNamespace="Microsoft.NetCore.Analyzers">
<Rule Id="CA1303" Action="Hidden" />
</Rules>
</RuleSet>
38 changes: 24 additions & 14 deletions docfx/current/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

## Breaking changes
With the 10.* release the Ubiquity.NET.Llvm.* libs have made a number of breaking changes.
While these are mostly small and easily adapted to they are still a breaking change. Thus,
While these are mostly small and easily adapted to, they are still a breaking change. Thus,
these changes were held to only occur on a Major release. Despite the pain of updating code
we think the changes are worth the effort to create a cleaner simpler and more consistent library.

Expand All @@ -16,7 +16,7 @@ projects no longer maintained.
| Old Name | New Name |
|---------------------------|--------------|
| Ubiquity.NET.Llvm.Interop | Ubiquity.Net.Llvm.Interop |
| LibLLVM.dll | Ubiquity.Net.LibLLVM.dll |
| LibLLVM.dll | Ubiquity.Net.LibLLVM |
| Ubiquity.NET.Llvm | Ubiquity.Net.Llvm |

### Library initialization
Expand All @@ -27,17 +27,18 @@ is done through this interface. This prevents accidental use of the registration
initializing the library (as that's a guaranteed app crash!)

### C#8 and non-Nullable references
With the 10.* release the Ubiquity.NET.Llvm.* libs all updated to target .NET Standard 2.1 and C#8. This allows use of nullable types
to make nullability more explicit. This necessitated a few minor breaking changes in the object model surface.
With the 10.* release the Ubiquity.NET.Llvm.* libs all updated to target .NET Standard 2.1 and C#8. This allows
use of nullable types to make nullability more explicit. This necessitated a few minor breaking changes in the
object model surface.

| Name | Description |
|-----------------|--------------|
| DebugMemberInfo | Removed setters of non-nullable properties and added constructor to allow building the type with non-null values |


### Renamed instruction predicate enumerator values
The comparison instruction predicates `Ubiquity.NET.Llvm.Instructions.[Predicate|IntPredicate]`were renamed for greater consistency
and clarity (Some of the float predicates had 'Than' in the name while the integer counterparts did not. (See:
The comparison instruction predicates `Ubiquity.NET.Llvm.Instructions.[Predicate|IntPredicate]`were renamed for greater
consistency and clarity (Some of the float predicates had 'Than' in the name while the integer counterparts did not. (See:
[Bug #152](https://github.com/UbiquityDotNET/Llvm.NET/issues/152) for details.)

| Old Name | New Name |
Expand Down Expand Up @@ -73,21 +74,30 @@ a sized type is created, even if the size is 0 because no members are provided.
anonymous empty structs, used in many languages. To create a named opaque type then the overload with just the
name is used. This isn't expected to impact many consumers, other than the tests, but it is a breaking change.

#### Context.CreateConstantString()
The behavior of Context.CreateConstantString(string) has changed slightly. It now constructs a valid C string with
a null terminator, which is generally what would be expected of something called "string". (The (string,bool)
overload remains, to allow apps to be explicit with intent) Additionally, the ConstantDataSequential.IsString
property now reflects whether the string is a C string (terminating null but no embedded nulls) and the
ConstantDataSequential.IsI8Sequence was added to provide the previous behavior of IsString, which was simply that
the underlying sequence element type was i8 (with or without a terminator)

### Removed redundant APIs
LLVM has made additional APIs available in the standard LLVM-C library that are either identical to or functionality equivalent to
APIs that were custom in previous versions of the Ubiquity.NET.Llvm DLLs. This is only observable at the interop library layer where some
of the custom APIs were removed and replaced with the official ones.
LLVM has made additional APIs available in the standard LLVM-C library that are either identical to or functionality
equivalent to APIs that were custom in previous versions of the Ubiquity.NET.Llvm DLLs. This is only observable at
the interop library layer where some of the custom APIs were removed and replaced with the official ones.

| Removed custom API | New Official API |
|--------------------|------------------|
| LibLLVMFoo [TBD] | LLVMFoo [TBD] |

### Disabled ORCJIT LazyFunction binding
Unfortunately, the ORCJIT truly lazy function generation callback support is currently disabled. LLVM itself is transitioning to the ORCJIT v2 and in the process
broke the lazy function binding support (At least for Windows+COFF). Previously a workaround for the issue of the COFF exports was applied in the
Llvm.NET ORCJIT library code for symbol lookups. However, with ORCJIT v2 the JIT itself is doing lookups and it does so only for external symbols
assuming the symbols it generates internally will be exports, but are not (at least for COFF modules anyway).
For more details see the LLVM bugs [25493](https://bugs.llvm.org/show_bug.cgi?id=25493) and [28699](https://bugs.llvm.org/show_bug.cgi?id=28699)
Unfortunately, the ORCJIT truly lazy function generation callback support is currently disabled. LLVM itself is
transitioning to the ORCJIT v2 and in the process broke the lazy function binding support (At least for Windows+COFF).
Previously a workaround for the issue of the COFF exports was applied in the Llvm.NET ORCJIT library code for symbol
lookups. However, with ORCJIT v2 the JIT itself is doing lookups and it does so only for external symbols assuming the
symbols it generates internally will be exports, but are not (at least for COFF modules anyway). For more details see
the LLVM bugs [25493](https://bugs.llvm.org/show_bug.cgi?id=25493) and [28699](https://bugs.llvm.org/show_bug.cgi?id=28699)

## v8.0.1
### Bug Fixes
Expand Down
25 changes: 21 additions & 4 deletions src/Interop/LibLLVM/ValueBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extern "C"
LLVMBool LibLLVMIsConstantZeroValue( LLVMValueRef valueRef )
{
auto pConstant = dyn_cast< Constant >( unwrap( valueRef ) );
if( pConstant == nullptr )
if ( pConstant == nullptr )
return 0;

return pConstant->isZeroValue( ) ? 1 : 0;
Expand All @@ -35,15 +35,15 @@ extern "C"
void LibLLVMRemoveGlobalFromParent( LLVMValueRef valueRef )
{
auto pGlobal = dyn_cast< GlobalVariable >( unwrap( valueRef ) );
if( pGlobal == nullptr )
if ( pGlobal == nullptr )
return;

pGlobal->removeFromParent( );
}

LibLLVMValueKind LibLLVMGetValueKind( LLVMValueRef valueRef )
{
return static_cast<LibLLVMValueKind>( unwrap( valueRef )->getValueID( ) );
return static_cast< LibLLVMValueKind >( unwrap( valueRef )->getValueID( ) );
}

LLVMValueRef LibLLVMGetAliasee( LLVMValueRef Val )
Expand All @@ -55,7 +55,7 @@ extern "C"
void LibLLVMGlobalVariableAddDebugExpression( LLVMValueRef /*GlobalVariable*/ globalVar, LLVMMetadataRef exp )
{
auto gv = unwrap<GlobalVariable>( globalVar );
gv->addDebugInfo( unwrap<DIGlobalVariableExpression>( exp ));
gv->addDebugInfo( unwrap<DIGlobalVariableExpression>( exp ) );
}

void LibLLVMFunctionAppendBasicBlock( LLVMValueRef /*Function*/ function, LLVMBasicBlockRef block )
Expand Down Expand Up @@ -88,4 +88,21 @@ extern "C"
{
return unwrap( cacheRef )->lookup( unwrap( valueRef ) );
}

LLVMBool LibLLVMIsConstantCString( LLVMValueRef C )
{
return unwrap<ConstantDataSequential>( C )->isCString( );
}

uint32_t LibLLVMGetConstantDataSequentialElementCount( LLVMValueRef C )
{
return unwrap<ConstantDataSequential>( C )->getNumElements( );
}

const char* LibLLVMGetConstantDataSequentialRawData( LLVMValueRef C, size_t* Length )
{
StringRef Str = unwrap<ConstantDataSequential>( C )->getRawDataValues( );
*Length = Str.size( );
return Str.data( );
}
}
8 changes: 8 additions & 0 deletions src/Interop/LibLLVM/include/libllvm-c/ValueBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ extern "C" {
void LibLLVMDisposeValueCache( LibLLVMValueCacheRef cacheRef );
void LibLLVMValueCacheAdd( LibLLVMValueCacheRef cacheRef, LLVMValueRef value, intptr_t handle );
intptr_t LibLLVMValueCacheLookup( LibLLVMValueCacheRef cacheRef, LLVMValueRef valueRef );

// Detect if a ConstantDataSequentioal is a C string (i8 sequence terminated with \0 and no embedded \0)
LLVMBool LibLLVMIsConstantCString(LLVMValueRef C);

// Retrieve the number of elements in a ConstantDataSequential
uint32_t LibLLVMGetConstantDataSequentialElementCount( LLVMValueRef C );

const char* LibLLVMGetConstantDataSequentialRawData( LLVMValueRef C, size_t* Length );
#ifdef __cplusplus
}
#endif
Expand Down
45 changes: 38 additions & 7 deletions src/Interop/LlvmBindingsGenerator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;

using CommandLine;

Expand All @@ -30,14 +31,44 @@ private static int Run( Options options )
};

Diagnostics.Implementation = diagnostics;

// read in the binding configuration from the YAML file
// It is hoped, that going forward, the YAML file is the only thing that needs to change
// but either way, it helps keep the declarative part in a more easily edited format.
string configPath = Path.Combine( Path.GetDirectoryName( Assembly.GetExecutingAssembly().Location ), "BindingsConfig.yml");
var config = new ReadOnlyConfig( YamlConfiguration.ParseFrom( configPath ) );
var library = new LibLlvmGeneratorLibrary( config, options.LlvmRoot, options.ExtensionsRoot, options.OutputPath );
Driver.Run( library );

try
{
// read in the binding configuration from the YAML file
// It is hoped, that going forward, the YAML file is the only thing that needs to change
// but either way, it helps keep the declarative part in a more easily edited format.
var config = new ReadOnlyConfig( YamlConfiguration.ParseFrom( configPath ) );
var library = new LibLlvmGeneratorLibrary( config, options.LlvmRoot, options.ExtensionsRoot, options.OutputPath );
Driver.Run( library );
}
catch(IOException ioex)
{
Diagnostics.Error( ioex.Message );
}
catch(YamlDotNet.Core.SyntaxErrorException yamlex)
{
// Sadly the yaml exception message includes the location info in a format that doesn't match any standard tooling
// for parsing error messages, so unpack it to get just the message of interest and re-format
var matcher = new Regex(@"\(Line\: \d+, Col\: \d+, Idx\: \d+\) - \(Line\: \d+, Col\: \d+, Idx\: \d+\)\: (.*)\Z");
var result = matcher.Match( yamlex.Message );
if( result.Success )
{
Diagnostics.Error( "{0}({1},{2},{3},{4}): error CFG001: {5}"
, configPath
, yamlex.Start.Line
, yamlex.Start.Column
, yamlex.End.Line
, yamlex.End.Column
, result.Groups[ 1 ] );
}
else
{
// message didn't match expectations, best effort at this point...
Diagnostics.Error( yamlex.Message );
}
}

return diagnostics.ErrorCount;
/* TODO:
Auto merge the generated docs XML with the Hand edited API Docs as hand merging is tedious and error prone.
Expand Down
3 changes: 3 additions & 0 deletions src/Interop/LlvmBindingsGenerator/bindingsConfig.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,9 @@ FunctionBindings:
ParamTransforms:
- !Array { Name: InputData, Semantics: In }

- Name: LibLLVMGetConstantDataSequentialRawData
ReturnTransform: !Unsafe {}

# The HandleMap Lists the types of handles and their disposal semantics
#HandleMap:
# - !ContextHandle { HandleName: LLVMTypeRef}
Expand Down
47 changes: 47 additions & 0 deletions src/Ubiquity.NET.Llvm.Tests/CallTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// -----------------------------------------------------------------------
// <copyright file="CallTests.cs" company="Ubiquity.NET Contributors">
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

using System.Linq;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using Ubiquity.NET.Llvm.Instructions;
using Ubiquity.NET.Llvm.Types;
using Ubiquity.NET.Llvm.Values;

namespace Ubiquity.NET.Llvm.Tests
{
[TestClass]
public class CallTests
{
[TestMethod]
[TestCategory( "Instruction" )]
public void Create_varargs_function_with_arbitrary_params_succeeds()
{
using var ctx = new Context();
var module = ctx.CreateBitcodeModule();
var varArgsSig = ctx.GetFunctionType(ctx.VoidType, Enumerable.Empty<ITypeRef>(), true);
var callerSig = ctx.GetFunctionType(ctx.VoidType);

var function = module.CreateFunction( "VarArgFunc", varArgsSig );
var entryBlock = function.PrependBasicBlock("entry");
var bldr = new InstructionBuilder(entryBlock);
bldr.Return( );

var caller = module.CreateFunction("CallVarArgs", callerSig);
entryBlock = caller.PrependBasicBlock( "entry" );
bldr = new InstructionBuilder( entryBlock );
var callInstruction = bldr.Call( function, ctx.CreateConstant( 0 ), ctx.CreateConstant( 1 ), ctx.CreateConstant( 2 ) );
Assert.IsNotNull( callInstruction );

// operands of call are all arguments, plus the target function
Assert.AreEqual( 4, callInstruction.Operands.Count );
Assert.AreEqual( 0UL, callInstruction.Operands.GetOperand<ConstantInt>( 0 )!.ZeroExtendedValue );
Assert.AreEqual( 1UL, callInstruction.Operands.GetOperand<ConstantInt>( 1 )!.ZeroExtendedValue );
Assert.AreEqual( 2UL, callInstruction.Operands.GetOperand<ConstantInt>( 2 )!.ZeroExtendedValue );
}
}
}
Loading