1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 11:38:21 +02:00

O(1) EnumParser

This commit is contained in:
Brian Fiete 2024-03-22 07:46:59 -04:00
parent 757d6e09aa
commit 45035c69e9
3 changed files with 147 additions and 39 deletions

View file

@ -387,6 +387,8 @@ namespace System
code.Append("public static Result<T> Parse(StringView str, bool ignoreCase = false)\n"); code.Append("public static Result<T> Parse(StringView str, bool ignoreCase = false)\n");
code.Append("{\n"); code.Append("{\n");
code.Append("\tif (str.IsEmpty)\n");
code.Append("\t\treturn .Err;\n");
Type dscrType = typeof(int); Type dscrType = typeof(int);
int dscrOffset = 0; int dscrOffset = 0;
@ -399,58 +401,163 @@ namespace System
} }
} }
bool hadPayload = false; List<FieldInfo> caseList = scope .();
bool hadCases = false;
for (var fieldInfo in typeof(T).GetFields()) bool hasPayload = false;
{
if (var fieldTypeInst = fieldInfo.FieldType as TypeInstance)
{
if ((fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0))
{
code.Append("\tT result = default;\n");
hadPayload = true;
break;
}
}
}
int caseIdx = 0;
for (var fieldInfo in typeof(T).GetFields()) for (var fieldInfo in typeof(T).GetFields())
{ {
if (!fieldInfo.IsEnumCase) if (!fieldInfo.IsEnumCase)
continue; continue;
if (var fieldTypeInst = fieldInfo.FieldType as TypeInstance) if (var fieldTypeInst = fieldInfo.FieldType as TypeInstance)
{ {
hadCases = true; caseList.Add(fieldInfo);
bool hasPayload = (fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0); if ((fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0))
if (!hasPayload)
{ {
code.AppendF($"\tif (str.Equals(\"{fieldInfo.Name}\", ignoreCase))\n\t\treturn .Ok(.{fieldInfo.Name});\n"); hasPayload = true;
} }
else }
}
if (hasPayload)
code.Append("\tT result = default;\n");
if ((!hasPayload) && (caseList.Count > 0))
{
Type underlyingType = typeof(T).UnderlyingType;
if (underlyingType.Size < 4)
underlyingType = typeof(int);
code.AppendF($"\tif (((str[0].IsDigit) || (str[0] == '-')) && ({underlyingType}.Parse(str) case let .Ok(iVal)))\n");
code.Append("\t\treturn .Ok((.)iVal);\n");
}
else if (hasPayload)
{
code.Append("""
StringView nameStr;
StringView argStr = default;
int parenPos = str.IndexOf('(');
if (parenPos != -1)
{
nameStr = str.Substring(0, parenPos);
argStr = str.Substring(parenPos + 1);
}
else
nameStr = str;
int matchIdx = -1;
""");
}
for (int ignoreCasePass < 2)
{
if (caseList.IsEmpty)
break;
void GetFieldName(FieldInfo fieldInfo, String outName)
{
outName.Append(fieldInfo.Name);
if (ignoreCasePass == 0)
outName.ToLower();
}
if (ignoreCasePass == 0)
code.Append("\tif (ignoreCase)\n");
else
code.Append("\telse\n");
code.Append("\t{\n");
int numBuckets = 1;
Dictionary<int, CompactList<(int hash, int caseIdx)>> buckets = scope .();
void ClearBucketList()
{
for (var list in buckets.Values)
list.Dispose();
buckets.Clear();
}
defer ClearBucketList();
for (int bucketPass < caseList.Count * 2)
{
ClearBucketList();
numBuckets = (bucketPass % 2 == 0) ? (caseList.Count + bucketPass / 2) : (caseList.Count - bucketPass / 2 - 1);
for (int caseIdx < caseList.Count)
{ {
code.AppendF($"\tif (str.StartsWith(\"{fieldInfo.Name}(\", ignoreCase ? .OrdinalIgnoreCase : .Ordinal))\n\t{{\n"); var fieldInfo = caseList[caseIdx];
var fieldName = GetFieldName(fieldInfo, .. scope .(64));
int hashCode = fieldName.GetHashCode();
if (buckets.TryAdd(hashCode % numBuckets, ?, var entryList))
*entryList = default;
entryList.Add((hashCode, caseIdx));
}
// Try for at least a 75% occupancy to ensure we get a jump table
if (buckets.Count >= numBuckets * 0.75)
break;
}
StringView strName;
if (hasPayload)
strName = (ignoreCasePass == 0) ? "checkStr" : "nameStr";
else
strName = "str";
if (ignoreCasePass == 0)
code.AppendF($"\t\tString checkStr = scope .({(hasPayload ? "nameStr" : "str")})..ToLower();\n");
code.AppendF($"\t\tint hashCode = {strName}.GetHashCode();\n");
if (numBuckets > 1)
code.AppendF($"\t\tswitch (hashCode % {numBuckets})\n");
code.Append("\t\t{\n");
for (int bucketIdx < numBuckets)
{
if (!buckets.TryGet(bucketIdx, ?, var entryList))
continue;
if (numBuckets > 1)
code.AppendF($"\t\tcase {bucketIdx}:\n");
for (var entry in entryList)
{
var fieldInfo = caseList[entry.caseIdx];
var fieldName = GetFieldName(fieldInfo, .. scope .(64));
var fieldTypeInst = fieldInfo.FieldType as TypeInstance;
bool caseHasPayload = (fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0);
if (hasPayload)
code.AppendF($"\t\t\tif ((hashCode == {fieldName.GetHashCode()}) && ({strName} == \"{fieldName}\") && ({caseHasPayload ? "!" : ""}argStr.IsEmpty))\n");
else
code.AppendF($"\t\t\tif ((hashCode == {fieldName.GetHashCode()}) && ({strName} == \"{fieldName}\"))\n");
if (caseHasPayload)
code.AppendF($"\t\t\t\tmatchIdx = {entry.caseIdx};\n");
else
code.AppendF($"\t\t\t\treturn .Ok(.{fieldInfo.Name});\n");
}
}
code.Append("\t\t}\n");
code.Append("\t}\n");
}
if (hasPayload)
{
code.Append("\tswitch (matchIdx)\n");
code.Append("\t{\n");
for (var caseIdx < caseList.Count)
{
var fieldInfo = caseList[caseIdx];
var fieldTypeInst = fieldInfo.FieldType as TypeInstance;
bool caseHasPayload = (fieldTypeInst.IsTuple) && (fieldTypeInst.FieldCount > 0);
if (caseHasPayload)
{
code.AppendF($"\tcase {caseIdx}:\n");
code.AppendF($"\t\t*({dscrType}*)((uint8*)&result + {dscrOffset}) = {fieldInfo.MemberOffset};\n"); code.AppendF($"\t\t*({dscrType}*)((uint8*)&result + {dscrOffset}) = {fieldInfo.MemberOffset};\n");
code.AppendF($"\t\tvar itr = Try!(EnumFields(str.Substring({fieldInfo.Name.Length+1})));\n"); code.AppendF($"\t\tvar itr = Try!(EnumFields(str.Substring({fieldInfo.Name.Length+1})));\n");
for (var tupField in fieldTypeInst.GetFields()) for (var tupField in fieldTypeInst.GetFields())
code.AppendF($"\t\tTry!(ParseValue(ref itr, ref *({tupField.FieldType}*)((uint8*)&result + {tupField.MemberOffset})));\n"); code.AppendF($"\t\tTry!(ParseValue(ref itr, ref *({tupField.FieldType}*)((uint8*)&result + {tupField.MemberOffset})));\n");
code.Append("\t\treturn result;\n"); code.Append("\t\treturn result;\n");
code.Append("\t}\n");
} }
} }
code.Append("\t}\n");
caseIdx++;
}
if ((hadCases) && (!hadPayload))
{
Type underlyingType = typeof(T).UnderlyingType;
if (underlyingType.Size < 4)
underlyingType = typeof(int);
code.AppendF($"\tif ({underlyingType}.Parse(str) case let .Ok(iVal))\n");
code.Append("\t\treturn .Ok((.)iVal);\n");
} }
code.Append("\treturn .Err;\n"); code.Append("\treturn .Err;\n");

View file

@ -17003,7 +17003,8 @@ void BeMCContext::Generate(BeFunction* function)
auto castedInst = (BeValueScopeRetainInst*)inst; auto castedInst = (BeValueScopeRetainInst*)inst;
auto mcValue = GetOperand(castedInst->mValue, false, true); auto mcValue = GetOperand(castedInst->mValue, false, true);
auto vregInfo = GetVRegInfo(mcValue); auto vregInfo = GetVRegInfo(mcValue);
vregInfo->mValueScopeRetainedKind = BeMCValueScopeRetainKind_Soft; if (vregInfo != NULL)
vregInfo->mValueScopeRetainedKind = BeMCValueScopeRetainKind_Soft;
} }
break; break;
case BeValueScopeEndInst::TypeId: case BeValueScopeEndInst::TypeId:

View file

@ -3098,7 +3098,7 @@ void BfIRCodeGen::HandleNextCmd()
else else
FatalError("Value is not ConstantExpr"); FatalError("Value is not ConstantExpr");
} }
static int symbolCount = 0; static int symbolCount = 0;
symbolCount++; symbolCount++;
@ -3108,7 +3108,7 @@ void BfIRCodeGen::HandleNextCmd()
{ {
chars[i] = llvm::ConstantInt::get(charType, strContent[0][i]);; chars[i] = llvm::ConstantInt::get(charType, strContent[0][i]);;
} }
chars.push_back(llvm::ConstantInt::get(charType, 0)); chars.push_back(llvm::ConstantInt::get(charType, 0));
auto stringType = llvm::ArrayType::get(charType, chars.size()); auto stringType = llvm::ArrayType::get(charType, chars.size());
@ -3121,7 +3121,7 @@ void BfIRCodeGen::HandleNextCmd()
globalVar->setConstant(true); globalVar->setConstant(true);
globalVar->setLinkage(llvm::GlobalValue::LinkageTypes::ExternalLinkage); globalVar->setLinkage(llvm::GlobalValue::LinkageTypes::ExternalLinkage);
globalVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global); globalVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
SetResult(curId, llvm::ConstantExpr::getBitCast(globalVar, charType->getPointerTo())); SetResult(curId, llvm::ConstantExpr::getBitCast(globalVar, charType->getPointerTo()));
break; break;
} }
@ -3421,7 +3421,7 @@ void BfIRCodeGen::HandleNextCmd()
} }
auto func = mLLVMModule->getOrInsertFunction(funcName, vecType, vecType, vecType); auto func = mLLVMModule->getOrInsertFunction(funcName, vecType, vecType, vecType);
// Call intrinsic // Call intrinsic
llvm::SmallVector<llvm::Value*, 2> args; llvm::SmallVector<llvm::Value*, 2> args;
args.push_back(val0); args.push_back(val0);