333 lines
18 KiB
Python
333 lines
18 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Handles.HandlesBase import BaseHandle
|
||
import ast
|
||
import llvmlite.ir as ir
|
||
|
||
|
||
class MatchHandle(BaseHandle):
|
||
def _HandleMatchLlvm(self, Node):
|
||
Gen = self.Trans.LlvmGen
|
||
SubjectVal = self.HandleExprLlvm(Node.subject)
|
||
if not SubjectVal:
|
||
return
|
||
|
||
IsRenumMatch = False
|
||
RenumName = None
|
||
SubjectPtr = None
|
||
if isinstance(Node.subject, ast.Name):
|
||
VarName = Node.subject.id
|
||
TypeInfo = self.Trans.SymbolTable.lookup(VarName)
|
||
if TypeInfo and TypeInfo.IsRenum:
|
||
IsRenumMatch = True
|
||
RenumName = TypeInfo.Name
|
||
SubjectPtr = Gen._loadVar(VarName)
|
||
|
||
if not IsRenumMatch:
|
||
for case in Node.cases:
|
||
if isinstance(case.pattern, ast.MatchClass):
|
||
cls_node = case.pattern.cls
|
||
VariantName = None
|
||
if isinstance(cls_node, ast.Name):
|
||
VariantName = cls_node.id
|
||
elif isinstance(cls_node, ast.Attribute):
|
||
VariantName = cls_node.attr
|
||
if VariantName and self.Trans.SymbolTable.has(VariantName):
|
||
SymInfo = self.Trans.SymbolTable[VariantName]
|
||
if SymInfo.IsEnumMember and SymInfo.EnumName:
|
||
EnumName = SymInfo.EnumName
|
||
if self.Trans.SymbolTable.has(EnumName):
|
||
EnumInfo = self.Trans.SymbolTable[EnumName]
|
||
if EnumInfo.IsRenum:
|
||
IsRenumMatch = True
|
||
RenumName = EnumName
|
||
if SubjectPtr is None:
|
||
SubjectPtr = self.HandleExprLlvm(Node.subject)
|
||
break
|
||
|
||
if IsRenumMatch and SubjectPtr:
|
||
self._HandleRenumMatchLlvm(Node, RenumName, SubjectPtr)
|
||
return
|
||
|
||
if not isinstance(SubjectVal.type, ir.IntType):
|
||
try:
|
||
SubjectVal = Gen.builder.ptrtoint(SubjectVal, ir.IntType(64), name="match_subj")
|
||
SubjectVal = Gen.builder.trunc(SubjectVal, ir.IntType(32), name="match_subj_i32")
|
||
except Exception: # 回退:ptrtoint 失败时直接返回
|
||
return
|
||
SwitchIntType = SubjectVal.type
|
||
DefaultBB = Gen.func.append_basic_block(name="match.default")
|
||
AfterBB = Gen.func.append_basic_block(name="match.end")
|
||
CaseBBs = []
|
||
CaseValues = []
|
||
HasDefault = False
|
||
HasNoBreak = []
|
||
for i, case in enumerate(Node.cases):
|
||
pattern = case.pattern
|
||
if isinstance(pattern, ast.MatchValue):
|
||
Val = self.HandleExprLlvm(pattern.value)
|
||
if Val:
|
||
if isinstance(Val.type, ir.IntType):
|
||
if Val.type != SwitchIntType:
|
||
if Val.type.width > SwitchIntType.width:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
|
||
else:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant)
|
||
else:
|
||
CaseVal = Val
|
||
else:
|
||
try:
|
||
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}")
|
||
except Exception: # 回退:ptrtoint 失败时设默认值 0
|
||
CaseVal = ir.Constant(SwitchIntType, 0)
|
||
CaseValues.append(CaseVal)
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
elif isinstance(pattern, ast.MatchOr):
|
||
for j, SubPattern in enumerate(pattern.patterns):
|
||
if isinstance(SubPattern, ast.MatchValue):
|
||
Val = self.HandleExprLlvm(SubPattern.value)
|
||
if Val:
|
||
if isinstance(Val.type, ir.IntType):
|
||
if Val.type != SwitchIntType:
|
||
if Val.type.width > SwitchIntType.width:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
|
||
else:
|
||
CaseVal = ir.Constant(SwitchIntType, Val.constant)
|
||
else:
|
||
CaseVal = Val
|
||
else:
|
||
try:
|
||
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}_{j}")
|
||
except Exception: # 回退:ptrtoint 失败时设默认值 0
|
||
CaseVal = ir.Constant(SwitchIntType, 0)
|
||
CaseValues.append(CaseVal)
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}_{j}"))
|
||
elif isinstance(pattern, ast.MatchSingleton):
|
||
if pattern.value is None:
|
||
CaseValues.append(ir.Constant(SwitchIntType, 0))
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
def _HasNoBreak(stmts):
|
||
for stmt in stmts:
|
||
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
|
||
if isinstance(stmt.value.func, ast.Attribute):
|
||
if (isinstance(stmt.value.func.value, ast.Name) and
|
||
stmt.value.func.value.id == 'c' and
|
||
stmt.value.func.attr == 'NoBreak'):
|
||
return True
|
||
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
|
||
if _HasNoBreak(stmt.body):
|
||
return True
|
||
if getattr(stmt, 'orelse', None):
|
||
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
|
||
return True
|
||
return False
|
||
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
|
||
if not HasDefault:
|
||
CaseBBs.append(DefaultBB)
|
||
SwitchCases = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
|
||
switch_instr = Gen.builder.switch(SubjectVal, DefaultBB)
|
||
for val, bb in SwitchCases:
|
||
switch_instr.add_case(val, bb)
|
||
CaseIdx = 0
|
||
for i, case in enumerate(Node.cases):
|
||
pattern = case.pattern
|
||
if isinstance(pattern, ast.MatchOr):
|
||
NumSubCases = len(pattern.patterns)
|
||
for j in range(NumSubCases):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, (ast.MatchValue, ast.MatchSingleton)):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
if not HasDefault:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
Gen.builder.position_at_start(AfterBB)
|
||
|
||
def _HandleRenumMatchLlvm(self, Node, RenumName, SubjectPtr):
|
||
Gen = self.Trans.LlvmGen
|
||
if isinstance(SubjectPtr.type, ir.PointerType) and isinstance(SubjectPtr.type.pointee, ir.PointerType):
|
||
SubjectPtr = Gen._load(SubjectPtr, name="Load_match_subj")
|
||
tag_ptr = Gen.builder.gep(SubjectPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="match_tag_ptr")
|
||
TagVal = Gen._load(tag_ptr, name="match_tag_val")
|
||
DefaultBB = Gen.func.append_basic_block(name="match.default")
|
||
AfterBB = Gen.func.append_basic_block(name="match.end")
|
||
CaseBBs = []
|
||
CaseValues = []
|
||
CaseBindings = []
|
||
HasDefault = False
|
||
HasNoBreak = []
|
||
for i, case in enumerate(Node.cases):
|
||
pattern = case.pattern
|
||
bindings = []
|
||
if isinstance(pattern, ast.MatchClass):
|
||
cls_node = pattern.cls
|
||
VariantName = None
|
||
if isinstance(cls_node, ast.Name):
|
||
VariantName = cls_node.id
|
||
elif isinstance(cls_node, ast.Attribute):
|
||
VariantName = cls_node.attr
|
||
if VariantName:
|
||
TagValue = None
|
||
if self.Trans.SymbolTable.has(VariantName):
|
||
SymInfo = self.Trans.SymbolTable[VariantName]
|
||
if SymInfo.IsEnumMember:
|
||
TagValue = SymInfo.value
|
||
if TagValue is not None:
|
||
CaseValues.append(ir.Constant(ir.IntType(32), TagValue))
|
||
CaseBB = Gen.func.append_basic_block(name=f"match.case_{VariantName}")
|
||
CaseBBs.append(CaseBB)
|
||
NestedStructName = f"{RenumName}_{VariantName}"
|
||
if NestedStructName in Gen.structs:
|
||
members = Gen.class_members.get(NestedStructName, [])
|
||
payLoad_members = [(n, t) for n, t in members if n != '__tag']
|
||
for j, sub_pat in enumerate(pattern.patterns):
|
||
if isinstance(sub_pat, ast.MatchAs) and sub_pat.name and j < len(payLoad_members):
|
||
bindings.append((sub_pat.name, payLoad_members[j][0], payLoad_members[j][1], j))
|
||
CaseBindings.append(bindings)
|
||
else:
|
||
CaseValues.append(ir.Constant(ir.IntType(32), 0))
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
CaseBindings.append([])
|
||
elif isinstance(pattern, ast.MatchValue):
|
||
Val = self.HandleExprLlvm(pattern.value)
|
||
if Val:
|
||
if isinstance(Val.type, ir.IntType):
|
||
CaseVal = Val
|
||
else:
|
||
try:
|
||
CaseVal = Gen.builder.ptrtoint(Val, ir.IntType(32), name=f"case_val_{i}")
|
||
except Exception: # 回退:ptrtoint 失败时设默认值 0
|
||
CaseVal = ir.Constant(ir.IntType(32), 0)
|
||
CaseValues.append(CaseVal)
|
||
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
|
||
CaseBindings.append([])
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
CaseBindings.append([])
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
HasDefault = True
|
||
CaseBBs.append(DefaultBB)
|
||
CaseBindings.append([])
|
||
def _HasNoBreak(stmts):
|
||
for stmt in stmts:
|
||
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
|
||
if isinstance(stmt.value.func, ast.Attribute):
|
||
if (isinstance(stmt.value.func.value, ast.Name) and
|
||
stmt.value.func.value.id == 'c' and
|
||
stmt.value.func.attr == 'NoBreak'):
|
||
return True
|
||
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
|
||
if _HasNoBreak(stmt.body):
|
||
return True
|
||
if getattr(stmt, 'orelse', None):
|
||
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
|
||
return True
|
||
return False
|
||
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
|
||
if not HasDefault:
|
||
CaseBBs.append(DefaultBB)
|
||
SwitchCases = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
|
||
switch_instr = Gen.builder.switch(TagVal, DefaultBB)
|
||
for val, bb in SwitchCases:
|
||
switch_instr.add_case(val, bb)
|
||
CaseIdx = 0
|
||
for i, case in enumerate(Node.cases):
|
||
pattern = case.pattern
|
||
if isinstance(pattern, ast.MatchClass):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
bindings = CaseBindings[CaseIdx] if CaseIdx < len(CaseBindings) else []
|
||
cls_node = pattern.cls
|
||
VariantName = None
|
||
if isinstance(cls_node, ast.Name):
|
||
VariantName = cls_node.id
|
||
elif isinstance(cls_node, ast.Attribute):
|
||
VariantName = cls_node.attr
|
||
if VariantName:
|
||
NestedStructName = f"{RenumName}_{VariantName}"
|
||
if NestedStructName in Gen.structs:
|
||
NestedStructType = Gen.structs[NestedStructName]
|
||
NestedStructPtrType = ir.PointerType(NestedStructType)
|
||
variant_ptr = Gen.builder.bitcast(SubjectPtr, NestedStructPtrType, name=f"match_cast_{VariantName}")
|
||
for bind_name, member_name, member_type, member_idx in bindings:
|
||
elem_ptr = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), member_idx + 1)], name=f"match_{bind_name}")
|
||
Gen.variables[bind_name] = elem_ptr
|
||
self.HandleBodyLlvm(case.body)
|
||
for bind_name, _, _, _ in bindings:
|
||
if bind_name in Gen.variables:
|
||
del Gen.variables[bind_name]
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchValue):
|
||
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
|
||
Gen.builder.position_at_start(CaseBBs[CaseIdx])
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchAs):
|
||
if pattern.pattern is None:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
elif isinstance(pattern, ast.MatchSequence):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
self.HandleBodyLlvm(case.body)
|
||
if not Gen.builder.block.is_terminated:
|
||
if not HasNoBreak[i]:
|
||
Gen.builder.branch(AfterBB)
|
||
CaseIdx += 1
|
||
if not HasDefault:
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
|
||
Gen.builder.position_at_start(DefaultBB)
|
||
Gen.builder.branch(AfterBB)
|
||
Gen.builder.position_at_start(AfterBB)
|