Skip to content

Commit

Permalink
Implemented chibias pre-checking with parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
kekyo committed May 16, 2024
1 parent 638f194 commit e8204af
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 48 deletions.
22 changes: 15 additions & 7 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,27 @@ chibicc-toolchainは、以下のツールチェインプログラムで構成さ

* chibias: CILアセンブラ
* chibild: CILオブジェクトリンカ
* chibiar: CILオブジェクトアーカイバ

![chibicc-toolchain overview](Images/toolchain.png)

chibiasは、CILソースコード群('*.s')をほぼそのままオブジェクトファイル('*.o')として出力し、
CILソースコードの実質的なアセンブル処理は、chibildが行います。
従って、chibiasは構文チェックを行いません。

chibiasとchibildの奇妙な挙動は、実装の制約によるものですが、
ツールチェイン使用者にとって見れば、POSIXで想定されるasやldなどのツールチェインと対比させて使用方法を理解できるという強みがあります。
* chibiasは、パーサーによる構文チェックを行いますが、シンボルの妥当性など細かい検査を行いません。
* chibiasが出力するオブジェクトファイルは、実際には入力となるCILソースファイルをgzipで圧縮しただけです。
このことは、実際にオブジェクトファイルを `gzip -d` することで確かめることができます。
* 同様に、chibiarが出力するアーカイブファイルは、実際にはシンボルテーブルファイルを含めたzipファイルです。
全く同様に、`unzip` コマンドで確かめることができます。

このような、chibias, chibild, chibiarの奇妙な挙動は実装の制約によるものですが、
ツールチェイン使用者にとって見れば、POSIXで想定されるas,ld,arなどのツールチェインと対比させて使用方法を理解できるという強みがあります。

以降の説明では、CILアセンブリソースコードをchibildで直接使用して解説します。

### CILアセンブル処理

chibildは、複数のCILオブジェクトファイル(ソースコード)を入力として、アセンブルを行い、
chibildは、複数のCILオブジェクトファイル(またはCILソースファイル)を入力として、アセンブルを行い、
結果を.NETアセンブリとして出力します。
この時、参照アセンブリ群を指定して、オブジェクトファイルから参照出来るようにします。

Expand All @@ -82,13 +88,15 @@ chibildはchibiccのバックエンドアセンブラとして開発しました
CLIバージョンのツールチェインを、nugetからインストール出来ます

* chibias: [chibias-cli](https://www.nuget.org/packages/chibias-cli)
* chibild: [chibild-cli](https://www.nuget.org/packages/chibild-cli)
* chibild: [chibild-cli](https://www.nuget.org/packages/chibild-cli).
* chibiar: [chibiar-cli](https://www.nuget.org/packages/chibiar-cli).

(紛らわしいのですが、 'chibias-cil' や 'chibild-cil' ではありません :)
(紛らわしいのですが、 'chibias-cil' ではありません :)

```bash
$ dotnet tool install -g chibias-cli
$ dotnet tool install -g chibild-cli
$ dotnet tool install -g chibiar-cli
```

使用可能になったかどうかは、以下のように確認できます:
Expand Down Expand Up @@ -132,7 +140,7 @@ usage: cil-ecma-chibild [options] <input path> [<input path> ...]

* chibildの実際のコマンド名は、`cil-ecma-chibild` です。chibiarやchibiasも同様です。
この命名規則は、GNU binutilsになぞらえたものです。
* chibildは、コマンドラインで指摘された複数の入力ファイル(オブジェクト '*.o'、CILソース '*.s')
* chibildは、コマンドラインで指示された複数の入力ファイル(オブジェクト '*.o'、CILソース '*.s')
をアセンブルして、1つの.NETアセンブリにまとめます。
* 参照ライブラリ名 `-l` は、アーカイブファイルパス ('*.a') を含めて、先頭から順に評価されます。
この機能は、重複するシンボル(関数/グローバル変数)にも適用されます。
Expand Down
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,28 @@ chibicc-toolchain consists of the following toolchain programs:

* chibias: A CIL assembler.
* chibild: A CIL object linker.
* chibild: A CIL object archiver.

![chibicc-toolchain overview](Images/toolchain.png)

chibias outputs the CIL source code ('*.s') almost as is as an object file ('*.o'),
and chibild performs the actual assembly process of the CIL source code.
Therefore, chibias does not perform syntax checking.

The strange behavior of chibias and chibild is due to implementation limitations.
* chibias performs syntax checking by the parser, but does not perform detailed checking such as symbol validity.
* The '*.o' file output by chibias is actually just a compression with gzip format of the input CIL source file.
This can be verified by actually `gzip -d` the object file.
* Similarly, the '*.a' file output by chibiar is actually a zip file format including the symbol table file.
Exactly the same can be verified with the `unzip` command.

The strange internal behavior of chibias, chibild and chibiar is due to implementation limitations.
For toolchain users, the advantage is that they can contrast their usage with
that of the "as" and "ld" toolchains expected on POSIX.
that of the "as", "ld" and "ar" toolchains expected on POSIX.

In the following sections, the CIL assembly source code is used directly in chibild.

### CIL assembling

chibild takes multiple CIL object file (source codes) as input, performs assembly,
chibild takes multiple CIL object file (or CIL source file) as input, performs assembly,
and outputs the result as .NET assemblies.
At this time, reference assemblies can be specified so that they can be referenced from the CIL object file.

Expand All @@ -87,12 +93,14 @@ Install CLI via nuget:

* chibias: [chibias-cli](https://www.nuget.org/packages/chibias-cli)
* chibild: [chibild-cli](https://www.nuget.org/packages/chibild-cli).
* chibiar: [chibiar-cli](https://www.nuget.org/packages/chibiar-cli).

* (It is not `chibias-cil` and `chibild-cil` :)
* (It is NOT `chibias-cil` :)

```bash
$ dotnet tool install -g chibias-cli
$ dotnet tool install -g chibild-cli
$ dotnet tool install -g chibiar-cli
```

Then:
Expand Down
100 changes: 91 additions & 9 deletions chibias/chibias.core/Assembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,17 @@
//
/////////////////////////////////////////////////////////////////////////////////////

using System;
using chibicc.toolchain.IO;
using chibicc.toolchain.Logging;
using chibicc.toolchain.Parsing;
using chibicc.toolchain.Tokenizing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using chibicc.toolchain.Internal;

namespace chibias;

Expand All @@ -24,18 +33,91 @@ public bool Assemble(
string sourceFilePath,
bool isDryrun)
{
using var outputStream = isDryrun ?
null : ObjectStreamUtilities.OpenObjectStream(outputObjectFilePath, true);
var outputTemporaryFilePath =
Path.Combine(
CommonUtilities.GetDirectoryPath(outputObjectFilePath),
Guid.NewGuid().ToString("N"));
var count = 0;

var tasks = new[]
{
() =>
{
using (var inputStream = StreamUtilities.OpenStream(sourceFilePath, false))
{
var tr = new StreamReader(inputStream, Encoding.UTF8, true);

using var inputStream = StreamUtilities.OpenStream(sourceFilePath, false);
var parser = new CilParser(this.logger);
var _ = parser.Parse(
CilTokenizer.TokenizeAll("", sourceFilePath, tr),
false).
ToArray();

if (outputStream != null)
{
inputStream.CopyTo(outputStream);
if (!parser.CaughtError)
{
Interlocked.Increment(ref count);
}
}
},
() =>
{
using var outputStream = isDryrun ?
null : ObjectStreamUtilities.OpenObjectStream(outputTemporaryFilePath, true);

if (outputStream != null)
{
using (var inputStream = StreamUtilities.OpenStream(sourceFilePath, false))
{
inputStream.CopyTo(outputStream);
outputStream.Flush();
}
}

outputStream.Flush();
Interlocked.Increment(ref count);
},
};

try
{
Parallel.Invoke(tasks);
}
catch
{
try
{
File.Delete(outputTemporaryFilePath);
}
catch
{
}
throw;
}

return false;
if (!isDryrun)
{
if (count == 2)
{
try
{
File.Delete(outputObjectFilePath);
}
catch
{
}
File.Move(outputTemporaryFilePath, outputObjectFilePath);
}
else
{
try
{
File.Delete(outputTemporaryFilePath);
}
catch
{
}
}
}

return count == 2;
}
}
}
5 changes: 3 additions & 2 deletions chibild/chibild.core.Tests/LinkerTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//
/////////////////////////////////////////////////////////////////////////////////////

using chibicc.toolchain.Internal;
using chibicc.toolchain.Logging;
using chibild.Internal;
using System;
Expand Down Expand Up @@ -72,7 +73,7 @@ public static string RunCore(
coreLibPath, tmp2Path,
}.
Concat(additionalReferencePaths ?? Array.Empty<string>()).
Select(Utilities.GetDirectoryPath).
Select(CommonUtilities.GetDirectoryPath).
Distinct().
ToArray();
var libraryReferences = new[]
Expand Down Expand Up @@ -129,7 +130,7 @@ public static string RunCore(
var psi = new ProcessStartInfo()
{
FileName = Path.Combine(ArtifactsBasePath,
Utilities.IsInWindows ? "ildasm.exe" : "ildasm.linux-x64"),
CommonUtilities.IsInWindows ? "ildasm.exe" : "ildasm.linux-x64"),
Arguments = $"-utf8 -out={disassembledPath} {outputAssemblyPath}"
};

Expand Down
4 changes: 2 additions & 2 deletions chibild/chibild.core.Tests/LinkerTests_Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//
/////////////////////////////////////////////////////////////////////////////////////

using chibild.Internal;
using chibicc.toolchain.Internal;
using System;
using System.IO;
using System.Runtime.CompilerServices;
Expand All @@ -33,7 +33,7 @@ private string Run(
var appHostTemplatePath = Path.GetFullPath(
Path.Combine(
LinkerTestRunner.ArtifactsBasePath,
Utilities.IsInWindows ? "apphost.exe" : "apphost.linux-x64"));
CommonUtilities.IsInWindows ? "apphost.exe" : "apphost.linux-x64"));
var tf = TargetFramework.TryParse(targetFrameworkMoniker, out var tf1) ?
tf1 : throw new InvalidOperationException();
return new()
Expand Down
12 changes: 6 additions & 6 deletions chibild/chibild.core/CilLinker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ injectToAssemblyPath is { } injectPath ?
Concat(inputReferences.
OfType<LibraryPathReference>().
Where(ir => Path.GetExtension(ir.RelativePath) == ".dll").
Select(ir => Utilities.GetDirectoryPath(Path.Combine(baseInputPath, ir.RelativePath)))).
Select(ir => CommonUtilities.GetDirectoryPath(Path.Combine(baseInputPath, ir.RelativePath)))).
Distinct().
ToArray();

Expand Down Expand Up @@ -346,7 +346,7 @@ injectToAssemblyPath is { } injectPath ?
var outputAssemblyFullPath = Path.GetFullPath(outputAssemblyPath);
var outputAssemblyCandidateFullPath = requireAppHost?
Path.Combine(
Utilities.GetDirectoryPath(outputAssemblyFullPath),
CommonUtilities.GetDirectoryPath(outputAssemblyFullPath),
Path.GetFileNameWithoutExtension(outputAssemblyFullPath) + ".dll") :
outputAssemblyFullPath;

Expand Down Expand Up @@ -412,7 +412,7 @@ injectToAssemblyPath is { } injectPath ?
}

var outputAssemblyBasePath =
Utilities.GetDirectoryPath(outputAssemblyCandidateFullPath);
CommonUtilities.GetDirectoryPath(outputAssemblyCandidateFullPath);
try
{
if (!Directory.Exists(outputAssemblyBasePath))
Expand Down Expand Up @@ -490,7 +490,7 @@ injectToAssemblyPath is { } injectPath ?
if (cachedAssemblies.FirstOrDefault(
assembly => assembly.Name.Name == corlibName) is { } corlibAssembly)
{
var corlibBasePath = Utilities.GetDirectoryPath(
var corlibBasePath = CommonUtilities.GetDirectoryPath(
corlibAssembly.MainModule.FileName);

requiredAssemblyBasePaths = requiredAssemblyBasePaths.
Expand All @@ -501,7 +501,7 @@ injectToAssemblyPath is { } injectPath ?
Parallel.ForEach(cachedAssemblies.
SelectMany(assembly => assembly.Modules).
Where(module => requiredAssemblyBasePaths.
Contains(Utilities.GetDirectoryPath(module.FileName))),
Contains(CommonUtilities.GetDirectoryPath(module.FileName))),
module =>
{
var tp = Path.Combine(
Expand All @@ -519,7 +519,7 @@ injectToAssemblyPath is { } injectPath ?
foreach (var ext in new[] { ".pdb", ".mdb" })
{
var fp = Path.Combine(
Utilities.GetDirectoryPath(module.FileName),
CommonUtilities.GetDirectoryPath(module.FileName),
Path.GetFileNameWithoutExtension(module.FileName) + ext);
tp = Path.Combine(
outputAssemblyBasePath,
Expand Down
3 changes: 1 addition & 2 deletions chibild/chibild.core/Cli/CliOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

using chibicc.toolchain.Internal;
using chibicc.toolchain.Logging;
using chibild.Internal;
using System;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -376,7 +375,7 @@ static bool TryGetOptionArgument(
break;
case ObjectFilePathReference(var path):
options.OutputAssemblyPath = Path.Combine(
Utilities.GetDirectoryPath(path),
CommonUtilities.GetDirectoryPath(path),
Path.GetFileNameWithoutExtension(path) + ".dll");
break;
}
Expand Down
5 changes: 3 additions & 2 deletions chibild/chibild.core/Internal/MultipleSymbolReaderProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//
/////////////////////////////////////////////////////////////////////////////////////

using chibicc.toolchain.Internal;
using chibicc.toolchain.Logging;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Mdb;
Expand All @@ -15,7 +17,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using chibicc.toolchain.Logging;

namespace chibild.Internal;

Expand Down Expand Up @@ -44,7 +45,7 @@ public MultipleSymbolReaderProvider(ILogger logger) =>
where TSymbolReaderProvider : ISymbolReaderProvider
{
var path = Path.Combine(
Utilities.GetDirectoryPath(fullPath),
CommonUtilities.GetDirectoryPath(fullPath),
Path.GetFileNameWithoutExtension(fullPath) + extension);

try
Expand Down
Loading

0 comments on commit e8204af

Please sign in to comment.