Last active
August 18, 2020 00:22
-
-
Save omgtehlion/25219feeb1691b489f237178b82fadb1 to your computer and use it in GitHub Desktop.
this code generates a copy of officedaytime’s intrinsic guide, but for .NET Core
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using HtmlAgilityPack; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Net; | |
using System.Xml.Linq; | |
using System.Xml.XPath; | |
// generated page is hosted at https://drachev.com/officedaytime/ | |
// more details at https://habr.com/ru/post/507074/ (in Russian) | |
namespace ConsoleApp4 | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var htmlDir = @"html"; | |
Directory.CreateDirectory("html"); | |
if (!File.Exists(Path.Combine(htmlDir, "odt.html"))) { | |
using var client = new WebClient(); | |
Console.WriteLine($"Downloading HTML ..."); | |
client.DownloadFile("https://www.officedaytime.com/simd512e/", Path.Combine(htmlDir, "odt.html")); | |
} | |
var doc = new HtmlDocument(); | |
doc.Load(Path.Combine(htmlDir, "odt.html")); | |
var xmlDoc = XDocument.Load(@"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.1.0\ref\netcoreapp3.1\System.Runtime.Intrinsics.xml"); | |
var summaries = xmlDoc.XPathSelectElements("//summary"); | |
foreach (var intr in doc.DocumentNode.SelectNodes("//span[@class='intr' or @class='intr_S1' or @class='intr_S2']").ToArray()) { | |
var newParts = new List<string>(); | |
var parts = intr.InnerHtml.Split("<br>").Select(s => s.Trim()).ToArray(); | |
foreach (var part in parts) { | |
newParts.Add(part); | |
if (part != "") { | |
var p = Unbrace(part.Replace(" ", "")); | |
if (p.Contains("/")) { | |
var leftRight = new List<string>(); | |
var converted = p.Split("/").Select(pp => MapFunc(summaries, pp).FirstOrDefault()); | |
if (converted.Any(co => co != null)) | |
newParts.Add(string.Join(" / ", converted)); | |
} else { | |
newParts.AddRange(MapFunc(summaries, p)); | |
} | |
} | |
} | |
intr.InnerHtml = string.Join("<br>", newParts); | |
} | |
foreach (var a in doc.DocumentNode.SelectNodes("//a[@href]")) { | |
var origLink = a.Attributes["href"].Value; | |
if (!origLink.StartsWith("http")) | |
a.Attributes["href"].Value = new Uri(new Uri("https://www.officedaytime.com/simd512e/"), origLink).ToString(); | |
} | |
foreach (var a in doc.DocumentNode.SelectNodes("//img[@src]")) { | |
var origLink = a.Attributes["src"].Value; | |
if (!origLink.StartsWith("http") && !origLink.StartsWith("/")) { | |
var target = Path.Combine(htmlDir, origLink); | |
if (!File.Exists(target)) { | |
Directory.CreateDirectory(Path.GetDirectoryName(target)); | |
using var client = new WebClient(); | |
Console.WriteLine($"Downloading {origLink}..."); | |
client.DownloadFile("https://www.officedaytime.com/simd512e/" + origLink, target); | |
} | |
} | |
} | |
doc.DocumentNode.SelectSingleNode("html/head").AppendChild( | |
HtmlNode.CreateNode("<style type=\"text/css\">.dotnet { color:rgb(0,147,181) } .dotnet a { text-decoration:none }</style>")); | |
doc.DocumentNode.SelectSingleNode("html/head/title").InnerHtml += " adapted for .NET Core"; | |
foreach (var link in doc.DocumentNode.SelectNodes("html/head/link[@rel='alternate']")) | |
link.Remove(); | |
var helpDoc = new HtmlDocument(); | |
helpDoc.LoadHtml(@"<b>What is this page?</b> | |
<p> | |
This page is a copy of a great x64 SIMD guide from | |
<a href='https://www.officedaytime.com/simd512e/'>https://www.officedaytime.com/simd512e/</a>, but adapted for those of us who use C#. | |
</p> | |
<p> | |
I amended it to include the names of intrinsics from the <b>System.Runtime.Intrinsics.X86</b> namespace as of .NET Core 3.1 (future versions pending).<br /> | |
These names are hard to find in MSDN documentation, and they differ from both Intel’s instruction names and C++ intrinsics. Hence, here they are. | |
</p> | |
<p> | |
C# names, when awailable, are written below C++ intrinsics. | |
</p> | |
<p> | |
♯ — links to MSDN documentation are marked with a <i>sharp</i> sign. | |
</p> | |
<p> | |
Some methods not included here are available for AVX and AVX2. | |
If you can not find what you want, please try and change class name from <b>SseX</b> to <b>Avx</b> or <b>Avx2</b>. | |
<br> | |
Same deal with <b>Vector256</b>/<b>Vector128</b>. | |
<br> | |
<b>AVX512</b> is completely missing from .NET Core at this point. To highlight only AVX2 instructions, use <a href='?mf=0&mt=8&mc=0'>this link</a>. | |
</p>"); | |
doc.DocumentNode.SelectSingleNode("html/body/span").Remove(); | |
var replacedA = doc.DocumentNode.SelectSingleNode("html/body/a"); | |
replacedA.ParentNode.ReplaceChild(helpDoc.DocumentNode, replacedA); | |
var lastBr = doc.DocumentNode.SelectSingleNode("html/body/br[last()]"); | |
RemoveNodesAfter(lastBr); | |
lastBr.ParentNode.InsertAfter(HtmlNode.CreateNode( | |
@"<p>Original content by <a href='https://www.officedaytime.com/simd512e/'>daytime</a>. Instruction names © Intel, .net method names © Microsoft.</p>" | |
), lastBr); | |
doc.Save(Path.Combine(htmlDir, "index.html")); | |
} | |
static void RemoveNodesAfter(HtmlNode node) | |
{ | |
node = node.NextSibling; | |
while (node != null) { | |
var next = node.NextSibling; | |
node.Remove(); | |
node = next; | |
} | |
} | |
static string Unbrace(string s) | |
{ | |
var brace = s.IndexOf('('); | |
return brace != -1 ? s[..brace] : s; | |
} | |
static Dictionary<string, string> MyMap = new Dictionary<string, string> { | |
{ " _mm_set_epi64x _mm_set_epi32 _mm_set_epi16 _mm_set_epi8 _mm_set_pd _mm_set_ps ", "M:System.Runtime.Intrinsics.Vector256.Create" }, | |
{ " _mm_setzero_si128 _mm_setzero_pd _mm_setzero_ps " ,"P:System.Runtime.Intrinsics.Vector256`1.Zero" }, | |
{ " _mm_slli_si128 ", "M:System.Runtime.Intrinsics.X86.Sse2.ShiftLeftLogical128BitLane"}, | |
{ " _mm_srli_si128 ", "M:System.Runtime.Intrinsics.X86.Sse2.ShiftRightLogical128BitLane"}, | |
}; | |
static string[] MapFunc(IEnumerable<XElement> summaries, string p) | |
{ | |
p = $" {p} "; | |
var mapped = summaries | |
.Where(s => s.Value.Contains(p)) | |
.Select(s => Unbrace(s.Parent.Attribute("name").Value ?? "")) | |
.ToHashSet(); | |
if (mapped.Count == 0) | |
mapped = MyMap.Where(s => s.Key.Contains(p)).Select(s => s.Value).ToHashSet(); | |
return mapped.Select(s => $"<span class='dotnet'>{Shorten(s)} <a href='{MsdnLink(s)}' target='_blank'>♯</a></span>").ToArray(); | |
} | |
static string MsdnLink(string s) | |
{ | |
if (s[1] == ':') | |
s = s[2..]; | |
return $"https://docs.microsoft.com/en-us/dotnet/api/{s.Replace('`', '-').ToLower()}?view=netcore-3.1"; | |
} | |
static string Shorten(string s) | |
{ | |
if (s[1] == ':') | |
s = s[2..]; | |
if (s.StartsWith("System.Runtime.Intrinsics.X86.")) | |
return s["System.Runtime.Intrinsics.X86.".Length..]; | |
if (s.StartsWith("System.Runtime.Intrinsics.")) | |
return s["System.Runtime.Intrinsics.".Length..]; | |
return s; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment