Merge pull request #806 from cpunion/llvm-debug

Debug: fix struct vars debug, params modication, supports expressions, lexical scope/lifecycle
This commit is contained in:
xushiwei
2024-10-29 11:09:00 +08:00
committed by GitHub
19 changed files with 861 additions and 293 deletions

View File

@@ -99,9 +99,10 @@ jobs:
bash .github/workflows/test_llgo.sh bash .github/workflows/test_llgo.sh
- name: LLDB tests - name: LLDB tests
if: ${{startsWith(matrix.os, 'macos')}}
run: | run: |
echo "Test lldb with llgo plugin on ${{matrix.os}} with LLVM ${{matrix.llvm}}" echo "Test lldb with llgo plugin on ${{matrix.os}} with LLVM ${{matrix.llvm}}"
bash _lldb/runtest.sh bash _lldb/runtest.sh -v
- name: Test demos - name: Test demos
continue-on-error: true continue-on-error: true

3
.gitignore vendored
View File

@@ -26,6 +26,9 @@ build.dir/
# Test binary, built with `go test -c` # Test binary, built with `go test -c`
*.test *.test
# Debug symbols
*.dSYM
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
*.swp *.swp

View File

@@ -26,6 +26,8 @@ find_lldb() {
# Find LLDB 18+ # Find LLDB 18+
LLDB_PATH=$(find_lldb) LLDB_PATH=$(find_lldb)
echo "LLDB_PATH: $LLDB_PATH"
$LLDB_PATH --version
export LLDB_PATH export LLDB_PATH
# Default package path # Default package path
@@ -34,5 +36,5 @@ export DEFAULT_PACKAGE_PATH="./cl/_testdata/debug"
# Function to build the project # Function to build the project
build_project() { build_project() {
local package_path="$1" local package_path="$1"
LLGO_DEBUG=1 go run ./cmd/llgo build -o "${package_path}/out" "${package_path}" LLGO_DEBUG=1 go run ./cmd/llgo build -o "${package_path}/debug.out" "${package_path}"
} }

View File

@@ -1,70 +1,112 @@
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring # pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
from typing import List, Optional, Dict, Any, Tuple
import re import re
import lldb import lldb
def __lldb_init_module(debugger, _): def log(*args: Any, **kwargs: Any) -> None:
print(*args, **kwargs, flush=True)
def __lldb_init_module(debugger: lldb.SBDebugger, _: Dict[str, Any]) -> None:
debugger.HandleCommand( debugger.HandleCommand(
'command script add -f llgo_plugin.print_go_expression p') 'command script add -f llgo_plugin.print_go_expression p')
debugger.HandleCommand( debugger.HandleCommand(
'command script add -f llgo_plugin.print_all_variables v') 'command script add -f llgo_plugin.print_all_variables v')
def is_llgo_compiler(target): def is_llgo_compiler(_target: lldb.SBTarget) -> bool:
return True return True
module = target.GetModuleAtIndex(0)
# Check for specific sections or symbols that might be unique to LLGo
llgo_indicators = ["__llgo_", "runtime.llgo", "llgo."]
# Check sections
for i in range(module.GetNumSections()):
section = module.GetSectionAtIndex(i)
section_name = section.GetName()
if any(indicator in section_name for indicator in llgo_indicators):
return True
# Check symbols
for symbol in module.symbols:
symbol_name = symbol.GetName()
if any(indicator in symbol_name for indicator in llgo_indicators):
return True
# Check compile units
for i in range(module.GetNumCompileUnits()):
cu = module.GetCompileUnitAtIndex(i)
cu_name = cu.GetFileSpec().GetFilename()
print(f"Compile unit: {cu_name}")
# You can add more checks here if needed
print("LLGo Compiler not detected")
return False
def print_go_expression(debugger, command, result, _internal_dict): def get_indexed_value(value: lldb.SBValue, index: int) -> Optional[lldb.SBValue]:
target = debugger.GetSelectedTarget() if not value or not value.IsValid():
if not is_llgo_compiler(target): return None
result.AppendMessage("Not a LLGo compiled binary.")
return
frame = debugger.GetSelectedTarget().GetProcess( type_name = value.GetType().GetName()
).GetSelectedThread().GetSelectedFrame()
# Handle Go-style pointer member access if type_name.startswith('[]'): # Slice
command = re.sub(r'(\w+)\.(\w+)', lambda m: f'(*{m.group(1)}).{m.group( data_ptr = value.GetChildMemberWithName('data')
2)}' if is_pointer(frame, m.group(1)) else m.group(0), command) element_type = data_ptr.GetType().GetPointeeType()
element_size = element_type.GetByteSize()
var = frame.EvaluateExpression(command) ptr_value = int(data_ptr.GetValue(), 16)
element_address = ptr_value + index * element_size
if var.error.Success(): target = value.GetTarget()
formatted = format_value(var, debugger) return target.CreateValueFromAddress(
result.AppendMessage(formatted) f"element_{index}", lldb.SBAddress(element_address, target), element_type)
elif value.GetType().IsArrayType(): # Array
return value.GetChildAtIndex(index)
else: else:
result.AppendMessage(f"Error: {var.error}") return None
def print_all_variables(debugger, command, result, _internal_dict): def evaluate_expression(frame: lldb.SBFrame, expression: str) -> Optional[lldb.SBValue]:
parts = re.findall(r'\*|\w+|\(|\)|\[.*?\]|\.', expression)
def evaluate_part(i: int) -> Tuple[Optional[lldb.SBValue], int]:
nonlocal parts
value: Optional[lldb.SBValue] = None
while i < len(parts):
part = parts[i]
if part == '*':
sub_value, i = evaluate_part(i + 1)
if sub_value and sub_value.IsValid():
value = sub_value.Dereference()
else:
return None, i
elif part == '(':
depth = 1
j = i + 1
while j < len(parts) and depth > 0:
if parts[j] == '(':
depth += 1
elif parts[j] == ')':
depth -= 1
j += 1
value, i = evaluate_part(i + 1)
i = j - 1
elif part == ')':
return value, i + 1
elif part == '.':
if value is None:
value = frame.FindVariable(parts[i+1])
else:
value = value.GetChildMemberWithName(parts[i+1])
i += 2
elif part.startswith('['):
index = int(part[1:-1])
value = get_indexed_value(value, index)
i += 1
else:
if value is None:
value = frame.FindVariable(part)
else:
value = value.GetChildMemberWithName(part)
i += 1
if not value or not value.IsValid():
return None, i
return value, i
value, _ = evaluate_part(0)
return value
def print_go_expression(debugger: lldb.SBDebugger, command: str, result: lldb.SBCommandReturnObject, _internal_dict: Dict[str, Any]) -> None:
frame = debugger.GetSelectedTarget().GetProcess(
).GetSelectedThread().GetSelectedFrame()
value = evaluate_expression(frame, command)
if value and value.IsValid():
result.AppendMessage(format_value(value, debugger))
else:
result.AppendMessage(
f"Error: Unable to evaluate expression '{command}'")
def print_all_variables(debugger: lldb.SBDebugger, _command: str, result: lldb.SBCommandReturnObject, _internal_dict: Dict[str, Any]) -> None:
target = debugger.GetSelectedTarget() target = debugger.GetSelectedTarget()
if not is_llgo_compiler(target): if not is_llgo_compiler(target):
result.AppendMessage("Not a LLGo compiled binary.") result.AppendMessage("Not a LLGo compiled binary.")
@@ -72,9 +114,9 @@ def print_all_variables(debugger, command, result, _internal_dict):
frame = debugger.GetSelectedTarget().GetProcess( frame = debugger.GetSelectedTarget().GetProcess(
).GetSelectedThread().GetSelectedFrame() ).GetSelectedThread().GetSelectedFrame()
variables = frame.GetVariables(True, True, True, False) variables = frame.GetVariables(True, True, True, True)
output = [] output: List[str] = []
for var in variables: for var in variables:
type_name = map_type_name(var.GetType().GetName()) type_name = map_type_name(var.GetType().GetName())
formatted = format_value(var, debugger, include_type=False, indent=0) formatted = format_value(var, debugger, include_type=False, indent=0)
@@ -83,14 +125,12 @@ def print_all_variables(debugger, command, result, _internal_dict):
result.AppendMessage("\n".join(output)) result.AppendMessage("\n".join(output))
def is_pointer(frame, var_name): def is_pointer(frame: lldb.SBFrame, var_name: str) -> bool:
var = frame.FindVariable(var_name) var = frame.FindVariable(var_name)
return var.IsValid() and var.GetType().IsPointerType() return var.IsValid() and var.GetType().IsPointerType()
# Format functions extracted from main.py
def format_value(var: lldb.SBValue, debugger: lldb.SBDebugger, include_type: bool = True, indent: int = 0) -> str:
def format_value(var, debugger, include_type=True, indent=0):
if not var.IsValid(): if not var.IsValid():
return "<variable not available>" return "<variable not available>"
@@ -98,8 +138,15 @@ def format_value(var, debugger, include_type=True, indent=0):
type_class = var_type.GetTypeClass() type_class = var_type.GetTypeClass()
type_name = map_type_name(var_type.GetName()) type_name = map_type_name(var_type.GetName())
# Handle typedef types
original_type_name = type_name
while var_type.IsTypedefType():
var_type = var_type.GetTypedefedType()
type_name = map_type_name(var_type.GetName())
type_class = var_type.GetTypeClass()
if var_type.IsPointerType(): if var_type.IsPointerType():
return format_pointer(var, debugger, indent, type_name) return format_pointer(var, debugger, indent, original_type_name)
if type_name.startswith('[]'): # Slice if type_name.startswith('[]'): # Slice
return format_slice(var, debugger, indent) return format_slice(var, debugger, indent)
@@ -108,7 +155,7 @@ def format_value(var, debugger, include_type=True, indent=0):
elif type_name == 'string': # String elif type_name == 'string': # String
return format_string(var) return format_string(var)
elif type_class in [lldb.eTypeClassStruct, lldb.eTypeClassClass]: elif type_class in [lldb.eTypeClassStruct, lldb.eTypeClassClass]:
return format_struct(var, debugger, include_type, indent, type_name) return format_struct(var, debugger, include_type, indent, original_type_name)
else: else:
value = var.GetValue() value = var.GetValue()
summary = var.GetSummary() summary = var.GetSummary()
@@ -120,10 +167,13 @@ def format_value(var, debugger, include_type=True, indent=0):
return "<variable not available>" return "<variable not available>"
def format_slice(var, debugger, indent): def format_slice(var: lldb.SBValue, debugger: lldb.SBDebugger, indent: int) -> str:
length = int(var.GetChildMemberWithName('len').GetValue()) length = var.GetChildMemberWithName('len').GetValue()
if length is None:
return "<variable not available>"
length = int(length)
data_ptr = var.GetChildMemberWithName('data') data_ptr = var.GetChildMemberWithName('data')
elements = [] elements: List[str] = []
ptr_value = int(data_ptr.GetValue(), 16) ptr_value = int(data_ptr.GetValue(), 16)
element_type = data_ptr.GetType().GetPointeeType() element_type = data_ptr.GetType().GetPointeeType()
@@ -152,8 +202,8 @@ def format_slice(var, debugger, indent):
return result return result
def format_array(var, debugger, indent): def format_array(var: lldb.SBValue, debugger: lldb.SBDebugger, indent: int) -> str:
elements = [] elements: List[str] = []
indent_str = ' ' * indent indent_str = ' ' * indent
next_indent_str = ' ' * (indent + 1) next_indent_str = ' ' * (indent + 1)
@@ -166,27 +216,28 @@ def format_array(var, debugger, indent):
element_type = map_type_name(var.GetType().GetArrayElementType().GetName()) element_type = map_type_name(var.GetType().GetArrayElementType().GetName())
type_name = f"[{array_size}]{element_type}" type_name = f"[{array_size}]{element_type}"
if len(elements) > 5: # 如果元素数量大于5则进行折行显示 if len(elements) > 5: # wrap line if too many elements
return f"{type_name}{{\n{next_indent_str}" + f",\n{next_indent_str}".join(elements) + f"\n{indent_str}}}" return f"{type_name}{{\n{next_indent_str}" + f",\n{next_indent_str}".join(elements) + f"\n{indent_str}}}"
else: else:
return f"{type_name}{{{', '.join(elements)}}}" return f"{type_name}{{{', '.join(elements)}}}"
def format_string(var): def format_string(var: lldb.SBValue) -> str:
summary = var.GetSummary() summary = var.GetSummary()
if summary is not None: if summary is not None:
return summary # Keep the quotes return summary # Keep the quotes
else: else:
data = var.GetChildMemberWithName('data').GetValue() data = var.GetChildMemberWithName('data').GetValue()
length = int(var.GetChildMemberWithName('len').GetValue()) length = var.GetChildMemberWithName('len').GetValue()
if data and length: if data and length:
length = int(length)
error = lldb.SBError() error = lldb.SBError()
return '"%s"' % var.process.ReadCStringFromMemory(int(data, 16), length + 1, error) return '"%s"' % var.process.ReadCStringFromMemory(int(data, 16), length + 1, error)
return '""' return "<variable not available>"
def format_struct(var, debugger, include_type=True, indent=0, type_name=""): def format_struct(var: lldb.SBValue, debugger: lldb.SBDebugger, include_type: bool = True, indent: int = 0, type_name: str = "") -> str:
children = [] children: List[str] = []
indent_str = ' ' * indent indent_str = ' ' * indent
next_indent_str = ' ' * (indent + 1) next_indent_str = ' ' * (indent + 1)
@@ -209,13 +260,13 @@ def format_struct(var, debugger, include_type=True, indent=0, type_name=""):
return struct_content return struct_content
def format_pointer(var, debugger, indent, type_name): def format_pointer(var: lldb.SBValue, _debugger: lldb.SBDebugger, _indent: int, _type_name: str) -> str:
if not var.IsValid() or var.GetValueAsUnsigned() == 0: if not var.IsValid() or var.GetValueAsUnsigned() == 0:
return "<variable not available>" return "<variable not available>"
return var.GetValue() # Return the address as a string return var.GetValue() # Return the address as a string
def map_type_name(type_name): def map_type_name(type_name: str) -> str:
# Handle pointer types # Handle pointer types
if type_name.endswith('*'): if type_name.endswith('*'):
base_type = type_name[:-1].strip() base_type = type_name[:-1].strip()
@@ -223,7 +274,7 @@ def map_type_name(type_name):
return f"*{mapped_base_type}" return f"*{mapped_base_type}"
# Map other types # Map other types
type_mapping = { type_mapping: Dict[str, str] = {
'long': 'int', 'long': 'int',
'void': 'unsafe.Pointer', 'void': 'unsafe.Pointer',
'char': 'byte', 'char': 'byte',

View File

@@ -8,5 +8,8 @@ source "$(dirname "$0")/common.sh"
executable="$1" executable="$1"
# Run LLDB # Get the directory of the current script
"$LLDB_PATH" "$executable" script_dir="$(dirname "$0")"
# Run LLDB with the LLGO plugin
"$LLDB_PATH" -O "command script import ${script_dir}/llgo_plugin.py" "$executable"

View File

@@ -4,7 +4,8 @@ set -e
# Source common functions and variables # Source common functions and variables
# shellcheck source=./_lldb/common.sh # shellcheck source=./_lldb/common.sh
source "$(dirname "$0")/common.sh" # shellcheck disable=SC1091
source "$(dirname "$0")/common.sh" || exit 1
# Parse command-line arguments # Parse command-line arguments
package_path="$DEFAULT_PACKAGE_PATH" package_path="$DEFAULT_PACKAGE_PATH"
@@ -34,23 +35,35 @@ while [[ $# -gt 0 ]]; do
done done
# Build the project # Build the project
build_project "$package_path" build_project "$package_path" || exit 1
# Set up the result file path
result_file="/tmp/lldb_exit_code"
# Prepare LLDB commands # Prepare LLDB commands
lldb_commands=( lldb_commands=(
"command script import _lldb/llgo_plugin.py"
"command script import _lldb/test.py" "command script import _lldb/test.py"
"script test.run_tests(\\\"${package_path}/out\\\", [\\\"${package_path}/in.go\\\"], ${verbose}, ${interactive}, ${plugin_path})" "script test.run_tests_with_result('${package_path}/debug.out', ['${package_path}/in.go'], $verbose, $interactive, $plugin_path, '$result_file')"
"quit"
) )
# Add quit command if not in interactive mode
if [ "$interactive" = False ]; then
lldb_commands+=("quit")
fi
# Run LLDB with prepared commands # Run LLDB with prepared commands
lldb_command_string="" lldb_command_string=""
for cmd in "${lldb_commands[@]}"; do for cmd in "${lldb_commands[@]}"; do
lldb_command_string+=" -O \"$cmd\"" lldb_command_string+=" -o \"$cmd\""
done done
# Run LLDB with the test script
eval "$LLDB_PATH $lldb_command_string" eval "$LLDB_PATH $lldb_command_string"
# Read the exit code from the result file
if [ -f "$result_file" ]; then
exit_code=$(cat "$result_file")
rm "$result_file"
exit "$exit_code"
else
echo "Error: Could not find exit code file"
exit 1
fi

View File

@@ -7,17 +7,14 @@ import signal
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List, Optional, Set, Dict, Any from typing import List, Optional, Set, Dict, Any
import lldb import lldb
import llgo_plugin # Add this import import llgo_plugin
from llgo_plugin import log
class LLDBTestException(Exception): class LLDBTestException(Exception):
pass pass
def log(*args: Any, **kwargs: Any) -> None:
print(*args, **kwargs, flush=True)
@dataclass @dataclass
class Test: class Test:
source_file: str source_file: str
@@ -60,7 +57,7 @@ class TestResults:
class LLDBDebugger: class LLDBDebugger:
def __init__(self, executable_path: str, plugin_path: Optional[str] = None): def __init__(self, executable_path: str, plugin_path: Optional[str] = None) -> None:
self.executable_path: str = executable_path self.executable_path: str = executable_path
self.plugin_path: Optional[str] = plugin_path self.plugin_path: Optional[str] = plugin_path
self.debugger: lldb.SBDebugger = lldb.SBDebugger.Create() self.debugger: lldb.SBDebugger = lldb.SBDebugger.Create()
@@ -70,7 +67,6 @@ class LLDBDebugger:
self.type_mapping: Dict[str, str] = { self.type_mapping: Dict[str, str] = {
'long': 'int', 'long': 'int',
'unsigned long': 'uint', 'unsigned long': 'uint',
# Add more mappings as needed
} }
def setup(self) -> None: def setup(self) -> None:
@@ -104,29 +100,14 @@ class LLDBDebugger:
def get_variable_value(self, var_expression: str) -> Optional[str]: def get_variable_value(self, var_expression: str) -> Optional[str]:
frame = self.process.GetSelectedThread().GetFrameAtIndex(0) frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
value = llgo_plugin.evaluate_expression(frame, var_expression)
parts = var_expression.split('.') if value and value.IsValid():
var = frame.FindVariable(parts[0]) return llgo_plugin.format_value(value, self.debugger)
return None
for part in parts[1:]:
if not var.IsValid():
return None
if '[' in part and ']' in part:
array_name, index = part.split('[')
index = int(index.rstrip(']'))
var = var.GetChildAtIndex(index)
elif var.GetType().IsPointerType():
var = var.Dereference()
var = var.GetChildMemberWithName(part)
else:
var = var.GetChildMemberWithName(part)
return llgo_plugin.format_value(var, self.debugger) if var.IsValid() else None
def get_all_variable_names(self) -> Set[str]: def get_all_variable_names(self) -> Set[str]:
frame = self.process.GetSelectedThread().GetFrameAtIndex(0) frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
return set(var.GetName() for var in frame.GetVariables(True, True, True, False)) return set(var.GetName() for var in frame.GetVariables(True, True, True, True))
def get_current_function_name(self) -> str: def get_current_function_name(self) -> str:
frame = self.process.GetSelectedThread().GetFrameAtIndex(0) frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
@@ -189,7 +170,7 @@ class LLDBDebugger:
def parse_expected_values(source_files: List[str]) -> List[TestCase]: def parse_expected_values(source_files: List[str]) -> List[TestCase]:
test_cases = [] test_cases: List[TestCase] = []
for source_file in source_files: for source_file in source_files:
with open(source_file, 'r', encoding='utf-8') as f: with open(source_file, 'r', encoding='utf-8') as f:
content = f.readlines() content = f.readlines()
@@ -198,7 +179,7 @@ def parse_expected_values(source_files: List[str]) -> List[TestCase]:
line = content[i].strip() line = content[i].strip()
if line.startswith('// Expected:'): if line.startswith('// Expected:'):
start_line = i + 1 start_line = i + 1
tests = [] tests: List[Test] = []
i += 1 i += 1
while i < len(content): while i < len(content):
line = content[i].strip() line = content[i].strip()
@@ -224,7 +205,8 @@ def execute_tests(executable_path: str, test_cases: List[TestCase], verbose: boo
debugger = LLDBDebugger(executable_path, plugin_path) debugger = LLDBDebugger(executable_path, plugin_path)
try: try:
if verbose: if verbose:
log(f"Setting breakpoint at {test_case.source_file}: {test_case.end_line}") log(
f"\nSetting breakpoint at {test_case.source_file}:{test_case.end_line}")
debugger.setup() debugger.setup()
debugger.set_breakpoint(test_case.source_file, test_case.end_line) debugger.set_breakpoint(test_case.source_file, test_case.end_line)
debugger.run_to_breakpoint() debugger.run_to_breakpoint()
@@ -259,7 +241,7 @@ def execute_tests(executable_path: str, test_cases: List[TestCase], verbose: boo
return results return results
def run_tests(executable_path: str, source_files: List[str], verbose: bool, interactive: bool, plugin_path: Optional[str]) -> None: def run_tests(executable_path: str, source_files: List[str], verbose: bool, interactive: bool, plugin_path: Optional[str]) -> int:
test_cases = parse_expected_values(source_files) test_cases = parse_expected_values(source_files)
if verbose: if verbose:
log(f"Running tests for {', '.join(source_files)} with {executable_path}") log(f"Running tests for {', '.join(source_files)} with {executable_path}")
@@ -269,12 +251,12 @@ def run_tests(executable_path: str, source_files: List[str], verbose: bool, inte
verbose, interactive, plugin_path) verbose, interactive, plugin_path)
print_test_results(results) print_test_results(results)
if results.total != results.passed: # Return 0 if all tests passed, 1 otherwise
os._exit(1) return 0 if results.failed == 0 else 1
def execute_test_case(debugger: LLDBDebugger, test_case: TestCase, all_variable_names: Set[str]) -> CaseResult: def execute_test_case(debugger: LLDBDebugger, test_case: TestCase, all_variable_names: Set[str]) -> CaseResult:
results = [] results: List[TestResult] = []
for test in test_case.tests: for test in test_case.tests:
if test.variable == "all variables": if test.variable == "all variables":
@@ -367,6 +349,24 @@ def print_test_result(result: TestResult, verbose: bool) -> None:
log(f" Actual: {result.actual}") log(f" Actual: {result.actual}")
def run_tests_with_result(executable_path: str, source_files: List[str], verbose: bool, interactive: bool, plugin_path: Optional[str], result_path: str) -> int:
try:
exit_code = run_tests(executable_path, source_files,
verbose, interactive, plugin_path)
except Exception as e:
log(f"An error occurred during test execution: {str(e)}")
exit_code = 2 # Use a different exit code for unexpected errors
try:
with open(result_path, 'w', encoding='utf-8') as f:
f.write(str(exit_code))
except IOError as e:
log(f"Error writing result to file {result_path}: {str(e)}")
# If we can't write to the file, we should still return the exit code
return exit_code
def main() -> None: def main() -> None:
log(sys.argv) log(sys.argv)
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@@ -378,12 +378,24 @@ def main() -> None:
parser.add_argument("-i", "--interactive", action="store_true", parser.add_argument("-i", "--interactive", action="store_true",
help="Enable interactive mode on test failure") help="Enable interactive mode on test failure")
parser.add_argument("--plugin", help="Path to the LLDB plugin") parser.add_argument("--plugin", help="Path to the LLDB plugin")
parser.add_argument("--result-path", help="Path to write the result")
args = parser.parse_args() args = parser.parse_args()
plugin_path = args.plugin or os.path.join(os.path.dirname( plugin_path = args.plugin or os.path.join(os.path.dirname(
os.path.realpath(__file__)), "go_lldb_plugin.py") os.path.realpath(__file__)), "go_lldb_plugin.py")
run_tests(args.executable, args.sources,
args.verbose, args.interactive, plugin_path) try:
if args.result_path:
exit_code = run_tests_with_result(args.executable, args.sources,
args.verbose, args.interactive, plugin_path, args.result_path)
else:
exit_code = run_tests(args.executable, args.sources,
args.verbose, args.interactive, plugin_path)
except Exception as e:
log(f"An unexpected error occurred: {str(e)}")
exit_code = 2 # Use a different exit code for unexpected errors
sys.exit(exit_code)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -20,7 +20,6 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/llgen" "github.com/goplus/llgo/internal/llgen"
) )
@@ -29,7 +28,7 @@ func main() {
fmt.Fprintln(os.Stderr, "Usage: llgen [flags] <pkg> [pkgPath]") fmt.Fprintln(os.Stderr, "Usage: llgen [flags] <pkg> [pkgPath]")
return return
} }
llgen.Init(build.IsDebugEnabled()) llgen.Init()
args := os.Args[1:] args := os.Args[1:]
llgen.SmartDoFile(args[0], args[1:]...) llgen.SmartDoFile(args[0], args[1:]...)
} }

View File

@@ -78,9 +78,10 @@ func FuncWithAllTypeStructParam(s StructWithAllTypeFields) {
// s.e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 30} // s.e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 30}
// s.pad1: 100 // s.pad1: 100
// s.pad2: 200 // s.pad2: 200
s.i8 = 8 s.i8 = '\b'
// Expected(skio): // Expected:
// s.i8: '\x08' // s.i8: '\b'
// s.i16: 2
println(len(s.s), s.i8) println(len(s.s), s.i8)
} }
@@ -115,6 +116,36 @@ func FuncWithAllTypeParams(
err error, err error,
fn func(string) (int, error), fn func(string) (int, error),
) (int, error) { ) (int, error) {
// Expected:
// all variables: i8 i16 i32 i64 i u8 u16 u32 u64 u f32 f64 b c64 c128 slice arr arr2 s e f pf pi intr m c err fn
// i32: 3
// i64: 4
// i: 5
// u32: 8
// u64: 9
// u: 10
// f32: 11
// f64: 12
// slice: []int{21, 22, 23}
// arr: [3]int{24, 25, 26}
// arr2: [3]github.com/goplus/llgo/cl/_testdata/debug.E{{i = 27}, {i = 28}, {i = 29}}
// slice[0]: 21
// slice[1]: 22
// slice[2]: 23
// arr[0]: 24
// arr[1]: 25
// arr[2]: 26
// arr2[0].i: 27
// arr2[1].i: 28
// arr2[2].i: 29
// e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 30}
// Expected(skip):
// i8: '\b'
// i16: 2
// u8: '\x06'
// u16: 7
// b: true
println( println(
i8, i16, i32, i64, i, u8, u16, u32, u64, u, i8, i16, i32, i64, i, u8, u16, u32, u64, u,
f32, f64, b, f32, f64, b,
@@ -127,32 +158,61 @@ func FuncWithAllTypeParams(
err, err,
fn, fn,
) )
// Expected:
// all variables: i8 i16 i32 i64 i u8 u16 u32 u64 u f32 f64 b c64 c128 slice arr arr2 s e f pf pi intr m c err fn
// i8: '\x01'
// i16: 2
// i32: 3
// i64: 4
// i: 5
// u8: '\x06'
// u16: 7
// u32: 8
// u64: 9
// u: 10
// f32: 11
// f64: 12
// b: true
// c64: complex64{real = 13, imag = 14}
// c128: complex128{real = 15, imag = 16}
// slice: []int{21, 22, 23}
// arr: [3]int{24, 25, 26}
// arr2: [3]github.com/goplus/llgo/cl/_testdata/debug.E{{i = 27}, {i = 28}, {i = 29}}
// s: "hello"
// e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 30}
i8 = 9 i8 = 9
i16 = 10
i32 = 11
i64 = 12
i = 13
u8 = 14
u16 = 15
u32 = 16
u64 = 17
u = 18
f32 = 19
f64 = 20
b = false
c64 = 21 + 22i
c128 = 23 + 24i
slice = []int{31, 32, 33}
arr = [3]int{34, 35, 36}
arr2 = [3]E{{i: 37}, {i: 38}, {i: 39}}
s = "world"
e = E{i: 40}
println(i8, i16, i32, i64, i, u8, u16, u32, u64, u,
f32, f64, b,
c64, c128,
slice, arr[0:], &arr2,
s,
&e,
&f, pf, pi, intr, m,
c,
err,
fn,
)
// Expected:
// i8: '\t'
// i16: 10
// i32: 11
// i64: 12
// i: 13
// u8: '\x0e'
// u16: 15
// u32: 16
// u64: 17
// u: 18
// f32: 19
// f64: 20
// b: false
// c64: complex64{real = 21, imag = 22}
// c128: complex128{real = 23, imag = 24}
// slice: []int{31, 32, 33}
// arr2: [3]github.com/goplus/llgo/cl/_testdata/debug.E{{i = 37}, {i = 38}, {i = 39}}
// s: "world"
// e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 40}
// Expected(skip): // Expected(skip):
// i8: '\x09' // arr: [3]int{34, 35, 36}
println(i8)
return 1, errors.New("some error") return 1, errors.New("some error")
} }
@@ -185,7 +245,7 @@ type BigStruct struct {
} }
func FuncStructParams(t TinyStruct, s SmallStruct, m MidStruct, b BigStruct) { func FuncStructParams(t TinyStruct, s SmallStruct, m MidStruct, b BigStruct) {
println(&t, &s, &m, &b) // println(&t, &s, &m, &b)
// Expected: // Expected:
// all variables: t s m b // all variables: t s m b
// t.I: 1 // t.I: 1
@@ -204,15 +264,45 @@ func FuncStructParams(t TinyStruct, s SmallStruct, m MidStruct, b BigStruct) {
// b.P: 14 // b.P: 14
// b.Q: 15 // b.Q: 15
// b.R: 16 // b.R: 16
println(t.I, s.I, s.J, m.I, m.J, m.K, b.I, b.J, b.K, b.L, b.M, b.N, b.O, b.P, b.Q, b.R)
t.I = 10 t.I = 10
// Expected(skip): s.I = 20
s.J = 21
m.I = 40
m.J = 41
m.K = 42
b.I = 70
b.J = 71
b.K = 72
b.L = 73
b.M = 74
b.N = 75
b.O = 76
b.P = 77
b.Q = 78
b.R = 79
// Expected:
// all variables: t s m b // all variables: t s m b
// t.I: 10 // t.I: 10
// s.I: 20
// s.J: 21
// m.I: 40
// m.J: 41
// m.K: 42
// b.I: 70
// b.J: 71
// b.K: 72
// b.L: 73
// b.M: 74
// b.N: 75
// b.O: 76
// b.P: 77
// b.Q: 78
// b.R: 79
println("done") println("done")
} }
func FuncStructPtrParams(t *TinyStruct, s *SmallStruct, m *MidStruct, b *BigStruct) { func FuncStructPtrParams(t *TinyStruct, s *SmallStruct, m *MidStruct, b *BigStruct) {
println(t, s, m, b)
// Expected: // Expected:
// all variables: t s m b // all variables: t s m b
// t.I: 1 // t.I: 1
@@ -231,13 +321,139 @@ func FuncStructPtrParams(t *TinyStruct, s *SmallStruct, m *MidStruct, b *BigStru
// b.P: 14 // b.P: 14
// b.Q: 15 // b.Q: 15
// b.R: 16 // b.R: 16
println(t, s, m, b)
t.I = 10 t.I = 10
s.I = 20
s.J = 21
m.I = 40
m.J = 41
m.K = 42
b.I = 70
b.J = 71
b.K = 72
b.L = 73
b.M = 74
b.N = 75
b.O = 76
b.P = 77
b.Q = 78
b.R = 79
// Expected: // Expected:
// all variables: t s m b // all variables: t s m b
// t.I: 10 // t.I: 10
// s.I: 20
// s.J: 21
// m.I: 40
// m.J: 41
// m.K: 42
// b.I: 70
// b.J: 71
// b.K: 72
// b.L: 73
// b.M: 74
// b.N: 75
// b.O: 76
// b.P: 77
// b.Q: 78
// b.R: 79
println(t.I, s.I, s.J, m.I, m.J, m.K, b.I, b.J, b.K, b.L, b.M, b.N, b.O, b.P, b.Q, b.R)
println("done") println("done")
} }
func ScopeIf(branch int) {
a := 1
// Expected:
// all variables: a branch
// a: 1
if branch == 1 {
b := 2
c := 3
// Expected:
// all variables: a b c branch
// a: 1
// b: 2
// c: 3
// branch: 1
println(a, b, c)
} else {
c := 3
d := 4
// Expected:
// all variables: a c d branch
// a: 1
// c: 3
// d: 4
// branch: 0
println(a, c, d)
}
// Expected:
// all variables: a branch
// a: 1
println("a:", a)
}
func ScopeFor() {
a := 1
for i := 0; i < 10; i++ {
switch i {
case 0:
println("i is 0")
// Expected:
// all variables: i a
// i: 0
// a: 1
println("i:", i)
case 1:
println("i is 1")
// Expected:
// all variables: i a
// i: 1
// a: 1
println("i:", i)
default:
println("i is", i)
}
}
println("a:", a)
}
func ScopeSwitch(i int) {
a := 0
switch i {
case 1:
b := 1
println("i is 1")
// Expected:
// all variables: i a b
// i: 1
// a: 0
// b: 1
println("i:", i, "a:", a, "b:", b)
case 2:
c := 2
println("i is 2")
// Expected:
// all variables: i a c
// i: 2
// a: 0
// c: 2
println("i:", i, "a:", a, "c:", c)
default:
d := 3
println("i is", i)
// Expected:
// all variables: i a d
// i: 3
// a: 0
// d: 3
println("i:", i, "a:", a, "d:", d)
}
// Expected:
// all variables: a i
// a: 0
println("a:", a)
}
func main() { func main() {
FuncStructParams(TinyStruct{I: 1}, SmallStruct{I: 2, J: 3}, MidStruct{I: 4, J: 5, K: 6}, BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16}) FuncStructParams(TinyStruct{I: 1}, SmallStruct{I: 2, J: 3}, MidStruct{I: 4, J: 5, K: 6}, BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16})
FuncStructPtrParams(&TinyStruct{I: 1}, &SmallStruct{I: 2, J: 3}, &MidStruct{I: 4, J: 5, K: 6}, &BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16}) FuncStructPtrParams(&TinyStruct{I: 1}, &SmallStruct{I: 2, J: 3}, &MidStruct{I: 4, J: 5, K: 6}, &BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16})
@@ -263,7 +479,7 @@ func main() {
arr2: [3]E{{i: 27}, {i: 28}, {i: 29}}, arr2: [3]E{{i: 27}, {i: 28}, {i: 29}},
s: "hello", s: "hello",
e: E{i: 30}, e: E{i: 30},
pf: &StructWithAllTypeFields{}, pf: &StructWithAllTypeFields{i16: 100},
pi: &i, pi: &i,
intr: &Struct{}, intr: &Struct{},
m: map[string]uint64{"a": 31, "b": 32}, m: map[string]uint64{"a": 31, "b": 32},
@@ -277,9 +493,36 @@ func main() {
pad1: 100, pad1: 100,
pad2: 200, pad2: 200,
} }
// Expected:
// all variables: s i err
// s.i8: '\x01'
// s.i16: 2
// s.i32: 3
// s.i64: 4
// s.i: 5
// s.u8: '\x06'
// s.u16: 7
// s.u32: 8
// s.u64: 9
// s.u: 10
// s.f32: 11
// s.f64: 12
// s.b: true
// s.c64: complex64{real = 13, imag = 14}
// s.c128: complex128{real = 15, imag = 16}
// s.slice: []int{21, 22, 23}
// s.arr: [3]int{24, 25, 26}
// s.arr2: [3]github.com/goplus/llgo/cl/_testdata/debug.E{{i = 27}, {i = 28}, {i = 29}}
// s.s: "hello"
// s.e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 30}
// s.pf.i16: 100
// *(s.pf).i16: 100
// *(s.pi): 100
globalStructPtr = &s globalStructPtr = &s
globalStruct = s globalStruct = s
println("globalInt:", globalInt) println("globalInt:", globalInt)
// Expected(skip):
// all variables: globalInt globalStruct globalStructPtr s i err
println("s:", &s) println("s:", &s)
FuncWithAllTypeStructParam(s) FuncWithAllTypeStructParam(s)
println("called function with struct") println("called function with struct")
@@ -298,18 +541,21 @@ func main() {
s.fn, s.fn,
) )
println(i, err) println(i, err)
println("called function with types") ScopeIf(1)
ScopeIf(0)
ScopeFor()
ScopeSwitch(1)
ScopeSwitch(2)
ScopeSwitch(3)
println(globalStructPtr) println(globalStructPtr)
println(&globalStruct) println(&globalStruct)
// Expected(skip):
// all variables: globalInt globalStruct globalStructPtr s i err
// s.i8: '\x01'
// s.i16: 2
s.i8 = 0x12 s.i8 = 0x12
println(s.i8) println(s.i8)
// Expected(skip): // Expected:
// all variables: globalInt globalStruct globalStructPtr s i err // all variables: s i err
// s.i8: '\x12' // s.i8: '\x12'
// Expected(skip):
// globalStruct.i8: '\x01' // globalStruct.i8: '\x01'
println((*globalStructPtr).i8) println((*globalStructPtr).i8)
println("done") println("done")

View File

@@ -238,6 +238,9 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
sig = types.NewSignatureType(nil, nil, nil, params, results, false) sig = types.NewSignatureType(nil, nil, nil, params, results, false)
} }
fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx, f.Origin() != nil) fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx, f.Origin() != nil)
if debugSymbols {
fn.Inline(llssa.NoInline)
}
} }
if nblk := len(f.Blocks); nblk > 0 { if nblk := len(f.Blocks); nblk > 0 {
@@ -260,8 +263,9 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
} }
b := fn.NewBuilder() b := fn.NewBuilder()
if debugSymbols { if debugSymbols {
b.DebugFunction(fn, p.goProg.Fset.Position(f.Pos())) pos := p.goProg.Fset.Position(f.Pos())
b.DISetCurrentDebugLocation(p.fn, p.goProg.Fset.Position(f.Pos())) bodyPos := p.getFuncBodyPos(f)
b.DebugFunction(fn, pos, bodyPos)
} }
p.bvals = make(map[ssa.Value]llssa.Expr) p.bvals = make(map[ssa.Value]llssa.Expr)
off := make([]int, len(f.Blocks)) off := make([]int, len(f.Blocks))
@@ -291,14 +295,56 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
return fn, nil, goFunc return fn, nil, goFunc
} }
func (p *context) getFuncBodyPos(f *ssa.Function) token.Position {
if f.Object() != nil {
return p.goProg.Fset.Position(f.Object().(*types.Func).Scope().Pos())
}
return p.goProg.Fset.Position(f.Pos())
}
func isGlobal(v *types.Var) bool {
// TODO(lijie): better implementation
return strings.HasPrefix(v.Parent().String(), "package ")
}
func (p *context) debugRef(b llssa.Builder, v *ssa.DebugRef) {
object := v.Object()
variable, ok := object.(*types.Var)
if !ok {
// Not a local variable.
return
}
if variable.IsField() {
// skip *ssa.FieldAddr
return
}
if isGlobal(variable) {
// avoid generate local variable debug info of global variable in function
return
}
pos := p.goProg.Fset.Position(v.Pos())
value := p.compileValue(b, v.X)
fn := v.Parent()
dbgVar := p.getLocalVariable(b, fn, variable)
scope := variable.Parent()
diScope := b.DIScope(p.fn, scope)
if v.IsAddr {
// *ssa.Alloc
b.DIDeclare(variable, value, dbgVar, diScope, pos, b.Func.Block(v.Block().Index))
} else {
b.DIValue(variable, value, dbgVar, diScope, pos, b.Func.Block(v.Block().Index))
}
}
func (p *context) debugParams(b llssa.Builder, f *ssa.Function) { func (p *context) debugParams(b llssa.Builder, f *ssa.Function) {
for i, param := range f.Params { for i, param := range f.Params {
variable := param.Object().(*types.Var)
pos := p.goProg.Fset.Position(param.Pos()) pos := p.goProg.Fset.Position(param.Pos())
v := p.compileValue(b, param) v := p.compileValue(b, param)
ty := param.Type() ty := param.Type()
argNo := i + 1 argNo := i + 1
div := b.DIVarParam(p.fn, pos, param.Name(), p.prog.Type(ty, llssa.InGo), argNo) div := b.DIVarParam(p.fn, pos, param.Name(), p.prog.Type(ty, llssa.InGo), argNo)
b.DIDeclare(v, div, p.fn, pos, p.fn.Block(0)) b.DIParam(variable, v, div, p.fn, pos, p.fn.Block(0))
} }
} }
@@ -483,11 +529,6 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
} }
log.Panicln("unreachable:", iv) log.Panicln("unreachable:", iv)
} }
if debugSymbols {
if v, ok := iv.(ssa.Instruction); ok {
b.DISetCurrentDebugLocation(p.fn, p.goProg.Fset.Position(v.Pos()))
}
}
switch v := iv.(type) { switch v := iv.(type) {
case *ssa.Call: case *ssa.Call:
ret = p.call(b, llssa.Call, &v.Call) ret = p.call(b, llssa.Call, &v.Call)
@@ -655,11 +696,27 @@ func (p *context) jumpTo(v *ssa.Jump) llssa.BasicBlock {
return fn.Block(succs[0].Index) return fn.Block(succs[0].Index)
} }
func (p *context) getDebugLocScope(v *ssa.Function, pos token.Pos) *types.Scope {
if v.Object() == nil {
return nil
}
funcScope := v.Object().(*types.Func).Scope()
return funcScope.Innermost(pos)
}
func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
if iv, ok := instr.(instrOrValue); ok { if iv, ok := instr.(instrOrValue); ok {
p.compileInstrOrValue(b, iv, false) p.compileInstrOrValue(b, iv, false)
return return
} }
if debugSymbols {
scope := p.getDebugLocScope(instr.Parent(), instr.Pos())
if scope != nil {
diScope := b.DIScope(p.fn, scope)
pos := p.fset.Position(instr.Pos())
b.DISetCurrentDebugLocation(diScope, pos)
}
}
switch v := instr.(type) { switch v := instr.(type) {
case *ssa.Store: case *ssa.Store:
va := v.Addr va := v.Addr
@@ -720,26 +777,7 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
b.Send(ch, x) b.Send(ch, x)
case *ssa.DebugRef: case *ssa.DebugRef:
if debugSymbols { if debugSymbols {
object := v.Object() p.debugRef(b, v)
variable, ok := object.(*types.Var)
if !ok {
// Not a local variable.
return
}
if variable.IsField() {
// skip *ssa.FieldAddr
return
}
pos := p.goProg.Fset.Position(v.Pos())
value := p.compileValue(b, v.X)
fn := v.Parent()
dbgVar := p.getLocalVariable(b, fn, variable)
if v.IsAddr {
// *ssa.Alloc
b.DIDeclare(value, dbgVar, p.fn, pos, b.Func.Block(v.Block().Index))
} else {
b.DIValue(value, dbgVar, p.fn, pos, b.Func.Block(v.Block().Index))
}
} }
default: default:
panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr)) panic(fmt.Sprintf("compileInstr: unknown instr - %T\n", instr))
@@ -755,7 +793,8 @@ func (p *context) getLocalVariable(b llssa.Builder, fn *ssa.Function, v *types.V
return b.DIVarParam(p.fn, pos, v.Name(), t, argNo) return b.DIVarParam(p.fn, pos, v.Name(), t, argNo)
} }
} }
return b.DIVarAuto(p.fn, pos, v.Name(), t) scope := b.DIScope(p.fn, v.Parent())
return b.DIVarAuto(scope, pos, v.Name(), t)
} }
func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) { func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) {

View File

@@ -273,6 +273,9 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObj
} }
sig := fn.Signature sig := fn.Signature
aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false, fn.Origin() != nil) aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false, fn.Origin() != nil)
if debugSymbols {
aFn.Inline(llssa.NoInline)
}
} }
} }
return return

View File

@@ -187,7 +187,14 @@ func Do(args []string, conf *Config) {
return dedup.Check(llssa.PkgPython).Types return dedup.Check(llssa.PkgPython).Types
}) })
progSSA := ssa.NewProgram(initial[0].Fset, ssaBuildMode) buildMode := ssaBuildMode
if cl.DebugSymbols() {
buildMode |= ssa.GlobalDebug
}
if !IsOptimizeEnabled() {
buildMode |= ssa.NaiveForm
}
progSSA := ssa.NewProgram(initial[0].Fset, buildMode)
patches := make(cl.Patches, len(altPkgPaths)) patches := make(cl.Patches, len(altPkgPaths))
altSSAPkgs(progSSA, patches, altPkgs[1:], verbose) altSSAPkgs(progSSA, patches, altPkgs[1:], verbose)
@@ -237,7 +244,7 @@ func isNeedRuntimeOrPyInit(pkg *packages.Package) (needRuntime, needPyInit bool)
} }
const ( const (
ssaBuildMode = ssa.SanityCheckFunctions | ssa.InstantiateGenerics | ssa.GlobalDebug ssaBuildMode = ssa.SanityCheckFunctions | ssa.InstantiateGenerics
) )
type context struct { type context struct {
@@ -438,7 +445,7 @@ func linkMainPkg(ctx *context, pkg *packages.Package, pkgs []*aPackage, llFiles
} }
args = append(args, exargs...) args = append(args, exargs...)
if cl.DebugSymbols() { if cl.DebugSymbols() {
args = append(args, "-gdwarf-5") args = append(args, "-gdwarf-4")
} }
// TODO(xsw): show work // TODO(xsw): show work
@@ -609,10 +616,22 @@ var (
) )
const llgoDebug = "LLGO_DEBUG" const llgoDebug = "LLGO_DEBUG"
const llgoOptimize = "LLGO_OPTIMIZE"
func isEnvOn(env string, defVal bool) bool {
envVal := strings.ToLower(os.Getenv(env))
if envVal == "" {
return defVal
}
return envVal == "1" || envVal == "true" || envVal == "on"
}
func IsDebugEnabled() bool { func IsDebugEnabled() bool {
llgoDbgVal := strings.ToLower(os.Getenv(llgoDebug)) return isEnvOn(llgoDebug, false)
return llgoDbgVal == "1" || llgoDbgVal == "true" || llgoDbgVal == "on" }
func IsOptimizeEnabled() bool {
return isEnvOn(llgoOptimize, true)
} }
func ParseArgs(args []string, swflags map[string]bool) (flags, patterns []string, verbose bool) { func ParseArgs(args []string, swflags map[string]bool) (flags, patterns []string, verbose bool) {

View File

@@ -20,16 +20,17 @@ import (
"os" "os"
"github.com/goplus/llgo/cl" "github.com/goplus/llgo/cl"
"github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/mod" "github.com/goplus/llgo/internal/mod"
llssa "github.com/goplus/llgo/ssa" llssa "github.com/goplus/llgo/ssa"
) )
func Init(enableDbg bool) { func Init() {
llssa.Initialize(llssa.InitAll) llssa.Initialize(llssa.InitAll)
llssa.SetDebug(llssa.DbgFlagAll) llssa.SetDebug(llssa.DbgFlagAll)
cl.SetDebug(cl.DbgFlagAll) cl.SetDebug(cl.DbgFlagAll)
cl.EnableDebugSymbols(enableDbg) cl.EnableDebugSymbols(build.IsDebugEnabled())
} }
func PkgPath(dir string) string { func PkgPath(dir string) string {

View File

@@ -25,6 +25,7 @@ import (
"strings" "strings"
"github.com/goplus/llgo/cl" "github.com/goplus/llgo/cl"
"github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/packages" "github.com/goplus/llgo/internal/packages"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
@@ -90,7 +91,14 @@ func genFrom(fileOrPkg string, pkgPath string) string {
initial, err := packages.LoadEx(dedup, prog.TypeSizes, cfg, fileOrPkg) initial, err := packages.LoadEx(dedup, prog.TypeSizes, cfg, fileOrPkg)
check(err) check(err)
_, pkgs := ssautil.AllPackages(initial, ssa.SanityCheckFunctions|ssa.InstantiateGenerics|ssa.GlobalDebug) buildMode := ssa.SanityCheckFunctions | ssa.InstantiateGenerics
if build.IsDebugEnabled() {
buildMode |= ssa.GlobalDebug
}
if !build.IsOptimizeEnabled() {
buildMode |= ssa.NaiveForm
}
_, pkgs := ssautil.AllPackages(initial, buildMode)
pkg := initial[0] pkg := initial[0]
ssaPkg := pkgs[0] ssaPkg := pkgs[0]

View File

@@ -17,7 +17,6 @@
package ssa package ssa
import ( import (
"go/token"
"go/types" "go/types"
"log" "log"
"strconv" "strconv"
@@ -281,7 +280,8 @@ func (p Function) NewBuilder() Builder {
b := prog.ctx.NewBuilder() b := prog.ctx.NewBuilder()
// TODO(xsw): Finalize may cause panic, so comment it. // TODO(xsw): Finalize may cause panic, so comment it.
// b.Finalize() // b.Finalize()
return &aBuilder{b, nil, p, p.Pkg, prog, make(map[Expr]dbgExpr)} return &aBuilder{b, nil, p, p.Pkg, prog,
make(map[Expr]dbgExpr), make(map[*types.Scope]DIScope)}
} }
// HasBody reports whether the function has a body. // HasBody reports whether the function has a body.
@@ -334,33 +334,24 @@ func (p Function) SetRecover(blk BasicBlock) {
p.recov = blk p.recov = blk
} }
func (p Function) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta { // -----------------------------------------------------------------------------
if p.diFunc == nil {
paramTypes := make([]llvm.Metadata, len(p.params)) type inlineAttr int
for i, t := range p.params {
paramTypes[i] = b.diType(t, pos).ll const (
} NoInline inlineAttr = iota
diFuncType := b.di.CreateSubroutineType(llvm.DISubroutineType{ AlwaysInline
File: b.file(pos.Filename).ll, InlineHint
Parameters: paramTypes, )
})
p.diFunc = &aDIFunction{ func (p Function) Inline(inline inlineAttr) {
b.di.CreateFunction( inlineAttrName := map[inlineAttr]string{
b.file(pos.Filename).ll, NoInline: "noinline",
llvm.DIFunction{ AlwaysInline: "alwaysinline",
Type: diFuncType, InlineHint: "inlinehint",
Name: p.Name(), }[inline]
LinkageName: p.Name(), inlineAttr := p.Pkg.mod.Context().CreateEnumAttribute(llvm.AttributeKindID(inlineAttrName), 0)
File: b.file(pos.Filename).ll, p.impl.AddFunctionAttr(inlineAttr)
Line: pos.Line,
IsDefinition: true,
Optimized: false,
},
),
}
p.impl.SetSubprogram(p.diFunc.ll)
}
return &aDIScopeMeta{p.diFunc.ll}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

298
ssa/di.go
View File

@@ -6,6 +6,7 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"path/filepath" "path/filepath"
"unsafe"
"github.com/goplus/llvm" "github.com/goplus/llvm"
) )
@@ -19,6 +20,7 @@ type aDIBuilder struct {
prog Program prog Program
types map[Type]DIType types map[Type]DIType
positioner Positioner positioner Positioner
m llvm.Module // Add this field
} }
type diBuilder = *aDIBuilder type diBuilder = *aDIBuilder
@@ -26,20 +28,21 @@ type diBuilder = *aDIBuilder
func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder { func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder {
m := pkg.mod m := pkg.mod
ctx := m.Context() ctx := m.Context()
m.AddNamedMetadataOperand("llvm.module.flags",
ctx.MDNode([]llvm.Metadata{ b := &aDIBuilder{
llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch di: llvm.NewDIBuilder(m),
ctx.MDString("Debug Info Version"), prog: prog,
llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(), types: make(map[*aType]DIType),
}), positioner: positioner,
) m: m, // Initialize the m field
m.AddNamedMetadataOperand("llvm.module.flags", }
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch b.addNamedMetadataOperand("llvm.module.flags", 2, "Debug Info Version", 3)
ctx.MDString("Dwarf Version"), b.addNamedMetadataOperand("llvm.module.flags", 7, "Dwarf Version", 4)
llvm.ConstInt(ctx.Int32Type(), 5, false).ConstantAsMetadata(), b.addNamedMetadataOperand("llvm.module.flags", 1, "wchar_size", 4)
}), b.addNamedMetadataOperand("llvm.module.flags", 8, "PIC Level", 2)
) b.addNamedMetadataOperand("llvm.module.flags", 7, "uwtable", 1)
b.addNamedMetadataOperand("llvm.module.flags", 7, "frame-pointer", 1)
// Add llvm.ident metadata // Add llvm.ident metadata
identNode := ctx.MDNode([]llvm.Metadata{ identNode := ctx.MDNode([]llvm.Metadata{
@@ -47,12 +50,19 @@ func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder {
}) })
m.AddNamedMetadataOperand("llvm.ident", identNode) m.AddNamedMetadataOperand("llvm.ident", identNode)
return &aDIBuilder{ return b
di: llvm.NewDIBuilder(m), }
prog: prog,
types: make(map[*aType]DIType), // New method to add named metadata operand
positioner: positioner, func (b diBuilder) addNamedMetadataOperand(name string, intValue int, stringValue string, intValue2 int) {
} ctx := b.m.Context()
b.m.AddNamedMetadataOperand(name,
ctx.MDNode([]llvm.Metadata{
llvm.ConstInt(ctx.Int32Type(), uint64(intValue), false).ConstantAsMetadata(),
ctx.MDString(stringValue),
llvm.ConstInt(ctx.Int32Type(), uint64(intValue2), false).ConstantAsMetadata(),
}),
)
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -77,7 +87,7 @@ func (b diBuilder) createCompileUnit(filename, dir string) CompilationUnit {
File: filename, File: filename,
Dir: dir, Dir: dir,
Producer: "LLGo", Producer: "LLGo",
Optimized: false, Optimized: true,
RuntimeVersion: 1, RuntimeVersion: 1,
})} })}
} }
@@ -125,10 +135,9 @@ func (b diBuilder) createType(name string, ty Type, pos token.Position) DIType {
case *types.Basic: case *types.Basic:
if t.Kind() == types.UnsafePointer { if t.Kind() == types.UnsafePointer {
typ = b.di.CreatePointerType(llvm.DIPointerType{ typ = b.di.CreatePointerType(llvm.DIPointerType{
Name: name, Name: name,
SizeInBits: b.prog.SizeOf(b.prog.rawType(t)) * 8, SizeInBits: b.prog.SizeOf(b.prog.rawType(t)) * 8,
AlignInBits: uint32(b.prog.sizes.Alignof(t) * 8), AlignInBits: uint32(b.prog.sizes.Alignof(t) * 8),
AddressSpace: 0,
}) })
return &aDIType{typ} return &aDIType{typ}
} }
@@ -158,9 +167,8 @@ func (b diBuilder) createType(name string, ty Type, pos token.Position) DIType {
case *types.Pointer: case *types.Pointer:
return b.createPointerType(name, b.prog.rawType(t.Elem()), pos) return b.createPointerType(name, b.prog.rawType(t.Elem()), pos)
case *types.Named: case *types.Named:
ty = b.prog.rawType(t.Underlying()) // Create typedef type for named types
pos = b.positioner.Position(t.Obj().Pos()) return b.createTypedefType(name, ty, pos)
return b.diTypeEx(name, ty, pos)
case *types.Interface: case *types.Interface:
ty := b.prog.rtType("Iface") ty := b.prog.rtType("Iface")
return b.createInterfaceType(name, ty) return b.createInterfaceType(name, ty)
@@ -180,6 +188,8 @@ func (b diBuilder) createType(name string, ty Type, pos token.Position) DIType {
case *types.Map: case *types.Map:
ty := b.prog.rtType("Map") ty := b.prog.rtType("Map")
return b.createMapType(name, ty, pos) return b.createMapType(name, ty, pos)
case *types.Tuple:
return b.createTupleType(name, ty, pos)
default: default:
panic(fmt.Errorf("can't create debug info of type: %v, %T", ty.RawType(), ty.RawType())) panic(fmt.Errorf("can't create debug info of type: %v, %T", ty.RawType(), ty.RawType()))
} }
@@ -194,6 +204,10 @@ type aDIFunction struct {
type DIFunction = *aDIFunction type DIFunction = *aDIFunction
func (p Function) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
return &aDIScopeMeta{p.diFunc.ll}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
type aDIGlobalVariableExpression struct { type aDIGlobalVariableExpression struct {
@@ -221,16 +235,28 @@ func (b diBuilder) createGlobalVariableExpression(scope DIScope, pos token.Posit
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
type aDILexicalBlock struct {
ll llvm.Metadata
}
type DILexicalBlock = *aDILexicalBlock
func (l *aDILexicalBlock) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta {
return &aDIScopeMeta{l.ll}
}
// ----------------------------------------------------------------------------
type aDIVar struct { type aDIVar struct {
ll llvm.Metadata ll llvm.Metadata
} }
type DIVar = *aDIVar type DIVar = *aDIVar
func (b diBuilder) createParameterVariable(f Function, pos token.Position, name string, argNo int, ty DIType) DIVar { func (b diBuilder) createParameterVariable(scope DIScope, pos token.Position, name string, argNo int, ty DIType) DIVar {
return &aDIVar{ return &aDIVar{
ll: b.di.CreateParameterVariable( ll: b.di.CreateParameterVariable(
f.scopeMeta(b, pos).ll, scope.scopeMeta(b, pos).ll,
llvm.DIParameterVariable{ llvm.DIParameterVariable{
Name: name, Name: name,
File: b.file(pos.Filename).ll, File: b.file(pos.Filename).ll,
@@ -258,6 +284,18 @@ func (b diBuilder) createAutoVariable(scope DIScope, pos token.Position, name st
} }
} }
func (b diBuilder) createTypedefType(name string, ty Type, pos token.Position) DIType {
underlyingType := b.diType(b.prog.rawType(ty.RawType().(*types.Named).Underlying()), pos)
typ := b.di.CreateTypedef(llvm.DITypedef{
Name: name,
Type: underlyingType.ll,
File: b.file(pos.Filename).ll,
Line: pos.Line,
AlignInBits: uint32(b.prog.sizes.Alignof(ty.RawType()) * 8),
})
return &aDIType{typ}
}
func (b diBuilder) createStringType() DIType { func (b diBuilder) createStringType() DIType {
ty := b.prog.rtType("String") ty := b.prog.rtType("String")
return b.doCreateStructType("string", ty, token.Position{}, func(ditStruct DIType) []llvm.Metadata { return b.doCreateStructType("string", ty, token.Position{}, func(ditStruct DIType) []llvm.Metadata {
@@ -362,12 +400,12 @@ func (b diBuilder) createComplexType(t Type) DIType {
} }
func (b diBuilder) createPointerType(name string, ty Type, pos token.Position) DIType { func (b diBuilder) createPointerType(name string, ty Type, pos token.Position) DIType {
ptrType := b.prog.VoidPtr()
return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{ return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{
Name: name, Name: name,
Pointee: b.diType(ty, pos).ll, Pointee: b.diType(ty, pos).ll,
SizeInBits: b.prog.SizeOf(ty) * 8, SizeInBits: b.prog.SizeOf(ptrType) * 8,
AlignInBits: uint32(b.prog.sizes.Alignof(ty.RawType())) * 8, AlignInBits: uint32(b.prog.sizes.Alignof(ptrType.RawType())) * 8,
AddressSpace: 0,
})} })}
} }
@@ -421,6 +459,26 @@ func (b diBuilder) createStructType(name string, ty Type, pos token.Position) (r
}) })
} }
func (b diBuilder) createTupleType(name string, ty Type, pos token.Position) DIType {
tupleType := ty.RawType().(*types.Tuple)
if tupleType.Len() == 0 {
return &aDIType{}
}
if tupleType.Len() == 1 {
t := b.prog.rawType(tupleType.At(0).Type())
return b.diType(t, pos)
}
return b.doCreateStructType(name, ty, pos, func(ditStruct DIType) []llvm.Metadata {
fields := make([]llvm.Metadata, ty.RawType().(*types.Tuple).Len())
for i := 0; i < ty.RawType().(*types.Tuple).Len(); i++ {
field := ty.RawType().(*types.Tuple).At(i)
tyField := b.prog.rawType(field.Type())
fields[i] = b.createMemberTypeEx(field.Name(), ty, tyField, i, pos, 0)
}
return fields
})
}
func (b diBuilder) createFuncPtrType(name string, ty Type, pos token.Position) DIType { func (b diBuilder) createFuncPtrType(name string, ty Type, pos token.Position) DIType {
ptr := b.prog.VoidPtr() ptr := b.prog.VoidPtr()
return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{ return &aDIType{ll: b.di.CreatePointerType(llvm.DIPointerType{
@@ -477,9 +535,9 @@ func (b diBuilder) diTypeEx(name string, t Type, pos token.Position) DIType {
return ty return ty
} }
func (b diBuilder) varParam(f Function, pos token.Position, varName string, vt DIType, argNo int) DIVar { func (b diBuilder) varParam(scope DIScope, pos token.Position, varName string, vt DIType, argNo int) DIVar {
return b.createParameterVariable( return b.createParameterVariable(
f, scope,
pos, pos,
varName, varName,
argNo, argNo,
@@ -487,8 +545,8 @@ func (b diBuilder) varParam(f Function, pos token.Position, varName string, vt D
) )
} }
func (b diBuilder) varAuto(f Function, pos token.Position, varName string, vt DIType) DIVar { func (b diBuilder) varAuto(scope DIScope, pos token.Position, varName string, vt DIType) DIVar {
return b.createAutoVariable(f, pos, varName, vt) return b.createAutoVariable(scope, pos, varName, vt)
} }
func (b diBuilder) file(filename string) DIFile { func (b diBuilder) file(filename string) DIFile {
@@ -510,21 +568,21 @@ func (b diBuilder) createExpression(ops []uint64) DIExpression {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Copy to alloca'd memory to get declareable address. // Copy to alloca'd memory to get declareable address.
func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, deref bool) { func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, exists bool) {
if v, ok := b.dbgVars[v]; ok { if v, ok := b.dbgVars[v]; ok {
return v.ptr, v.val, v.deref return v.ptr, v.val, true
} }
t := v.Type.RawType().Underlying() t := v.Type.RawType().Underlying()
dbgPtr, dbgVal, deref = b.doConstructDebugAddr(v, t) dbgPtr, dbgVal = b.doConstructDebugAddr(v, t)
b.dbgVars[v] = dbgExpr{dbgPtr, dbgVal, deref} dbgExpr := dbgExpr{dbgPtr, dbgVal}
return dbgPtr, dbgVal, deref b.dbgVars[v] = dbgExpr
b.dbgVars[dbgVal] = dbgExpr
return dbgPtr, dbgVal, false
} }
func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal Expr, deref bool) { func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal Expr) {
var ty Type var ty Type
switch t := t.(type) { switch t := t.(type) {
case *types.Pointer:
return v, v, false
case *types.Basic: case *types.Basic:
if t.Info()&types.IsComplex != 0 { if t.Info()&types.IsComplex != 0 {
if t.Kind() == types.Complex128 { if t.Kind() == types.Complex128 {
@@ -554,34 +612,111 @@ func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal
dbgPtr.Type = b.Prog.Pointer(v.Type) dbgPtr.Type = b.Prog.Pointer(v.Type)
b.Store(dbgPtr, v) b.Store(dbgPtr, v)
dbgVal = b.Load(dbgPtr) dbgVal = b.Load(dbgPtr)
return dbgPtr, dbgVal, deref return dbgPtr, dbgVal
} }
func (b Builder) di() diBuilder { func (b Builder) di() diBuilder {
return b.Pkg.di return b.Pkg.di
} }
func (b Builder) DIDeclare(v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) { func (b Builder) DIParam(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
dbgPtr, _, _ := b.constructDebugAddr(v) b.DIValue(variable, v, dv, scope, pos, blk)
}
func (b Builder) DIDeclare(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
expr := b.di().createExpression(nil) expr := b.di().createExpression(nil)
b.di().dbgDeclare(dbgPtr, dv, scope, pos, expr, blk) b.di().dbgDeclare(v, dv, scope, pos, expr, blk)
} }
func (b Builder) DIValue(v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) { func (b Builder) DIValue(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) {
expr := b.di().createExpression(nil) ty := v.Type.RawType().Underlying()
b.di().dbgValue(v, dv, scope, pos, expr, blk) if !needConstructAddr(ty) {
expr := b.di().createExpression(nil)
b.di().dbgValue(v, dv, scope, pos, expr, blk)
} else {
dbgPtr, _, _ := b.constructDebugAddr(v)
expr := b.di().createExpression([]uint64{opDeref})
b.di().dbgValue(dbgPtr, dv, scope, pos, expr, blk)
}
} }
func (b Builder) DIVarParam(f Function, pos token.Position, varName string, vt Type, argNo int) DIVar { const (
opDeref = 0x06
)
func needConstructAddr(t types.Type) bool {
switch t := t.(type) {
case *types.Basic:
if t.Info()&types.IsComplex != 0 {
return true
} else if t.Info()&types.IsString != 0 {
return true
}
return false
case *types.Pointer:
return false
default:
return true
}
}
func (b Builder) DIVarParam(scope DIScope, pos token.Position, varName string, vt Type, argNo int) DIVar {
t := b.di().diType(vt, pos) t := b.di().diType(vt, pos)
return b.di().varParam(f, pos, varName, t, argNo) return b.di().varParam(scope, pos, varName, t, argNo)
} }
func (b Builder) DIVarAuto(f Function, pos token.Position, varName string, vt Type) DIVar { func (b Builder) DIVarAuto(scope DIScope, pos token.Position, varName string, vt Type) DIVar {
t := b.di().diType(vt, pos) t := b.di().diType(vt, pos)
return b.di().varAuto(f, pos, varName, t) return b.di().varAuto(scope, pos, varName, t)
} }
// hack for types.Scope
type hackScope struct {
parent *types.Scope
children []*types.Scope
number int // parent.children[number-1] is this scope; 0 if there is no parent
elems map[string]types.Object // lazily allocated
pos, end token.Pos // scope extent; may be invalid
comment string // for debugging only
isFunc bool // set if this is a function scope (internal use only)
}
func isFunc(scope *types.Scope) bool {
hs := (*hackScope)(unsafe.Pointer(scope))
return hs.isFunc
}
func (b Builder) DIScope(f Function, scope *types.Scope) DIScope {
if cachedScope, ok := b.diScopeCache[scope]; ok {
return cachedScope
}
pos := b.di().positioner.Position(scope.Pos())
// skip package and universe scope
// if scope.Parent().Parent() == nil {
// return b.di().file(pos.Filename)
// }
var result DIScope
if isFunc(scope) {
// TODO(lijie): should check scope == function scope
result = f
} else {
parentScope := b.DIScope(f, scope.Parent())
result = &aDILexicalBlock{b.di().di.CreateLexicalBlock(parentScope.scopeMeta(b.di(), pos).ll, llvm.DILexicalBlock{
File: b.di().file(pos.Filename).ll,
Line: pos.Line,
Column: pos.Column,
})}
}
b.diScopeCache[scope] = result
return result
}
const (
MD_dbg = 0
)
func (b Builder) DIGlobal(v Expr, name string, pos token.Position) { func (b Builder) DIGlobal(v Expr, name string, pos token.Position) {
if _, ok := b.Pkg.glbDbgVars[v]; ok { if _, ok := b.Pkg.glbDbgVars[v]; ok {
return return
@@ -594,22 +729,55 @@ func (b Builder) DIGlobal(v Expr, name string, pos token.Position) {
v.Type, v.Type,
false, false,
) )
v.impl.AddMetadata(0, gv.ll) v.impl.AddMetadata(MD_dbg, gv.ll)
b.Pkg.glbDbgVars[v] = true b.Pkg.glbDbgVars[v] = true
} }
func (b Builder) DISetCurrentDebugLocation(f Function, pos token.Position) { func (b Builder) DISetCurrentDebugLocation(diScope DIScope, pos token.Position) {
b.impl.SetCurrentDebugLocation( b.impl.SetCurrentDebugLocation(
uint(pos.Line), uint(pos.Line),
uint(pos.Column), uint(pos.Column),
f.scopeMeta(b.di(), pos).ll, diScope.scopeMeta(b.di(), pos).ll,
f.impl.InstructionDebugLoc(), llvm.Metadata{},
) )
} }
func (b Builder) DebugFunction(f Function, pos token.Position) { func (b Builder) DebugFunction(f Function, pos token.Position, bodyPos token.Position) {
// attach debug info to function p := f
f.scopeMeta(b.Pkg.di, pos) if p.diFunc == nil {
sig := p.Type.raw.Type.(*types.Signature)
rt := p.Prog.Type(sig.Results(), InGo)
paramTypes := make([]llvm.Metadata, len(p.params)+1)
paramTypes[0] = b.di().diType(rt, pos).ll
for i, t := range p.params {
paramTypes[i+1] = b.di().diType(t, pos).ll
}
diFuncType := b.di().di.CreateSubroutineType(llvm.DISubroutineType{
File: b.di().file(pos.Filename).ll,
Parameters: paramTypes,
})
dif := llvm.DIFunction{
Type: diFuncType,
Name: p.Name(),
LinkageName: p.Name(),
File: b.di().file(pos.Filename).ll,
Line: pos.Line,
ScopeLine: bodyPos.Line,
IsDefinition: true,
LocalToUnit: true,
Optimized: true,
}
p.diFunc = &aDIFunction{
b.di().di.CreateFunction(b.di().file(pos.Filename).ll, dif),
}
p.impl.SetSubprogram(p.diFunc.ll)
}
b.impl.SetCurrentDebugLocation(
uint(bodyPos.Line),
uint(bodyPos.Column),
p.diFunc.ll,
f.impl.InstructionDebugLoc(),
)
} }
func (b Builder) Param(idx int) Expr { func (b Builder) Param(idx int) Expr {

View File

@@ -141,6 +141,10 @@ const (
) )
func (b Builder) getDefer(kind DoAction) *aDefer { func (b Builder) getDefer(kind DoAction) *aDefer {
if b.Func.recov == nil {
// b.Func.recov maybe nil in ssa.NaiveForm
return nil
}
self := b.Func self := b.Func
if self.defer_ == nil { if self.defer_ == nil {
// TODO(xsw): check if in pkg.init // TODO(xsw): check if in pkg.init
@@ -241,6 +245,9 @@ func (b Builder) Defer(kind DoAction, fn Expr, args ...Expr) {
// RunDefers emits instructions to run deferred instructions. // RunDefers emits instructions to run deferred instructions.
func (b Builder) RunDefers() { func (b Builder) RunDefers() {
self := b.getDefer(DeferInCond) self := b.getDefer(DeferInCond)
if self == nil {
return
}
blk := b.Func.MakeBlock() blk := b.Func.MakeBlock()
self.rundsNext = append(self.rundsNext, blk) self.rundsNext = append(self.rundsNext, blk)

View File

@@ -58,9 +58,8 @@ func (p BasicBlock) Addr() Expr {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
type dbgExpr struct { type dbgExpr struct {
ptr Expr ptr Expr
val Expr val Expr
deref bool
} }
type aBuilder struct { type aBuilder struct {
@@ -70,7 +69,8 @@ type aBuilder struct {
Pkg Package Pkg Package
Prog Program Prog Program
dbgVars map[Expr]dbgExpr dbgVars map[Expr]dbgExpr // save copied address and values for debug info
diScopeCache map[*types.Scope]DIScope // avoid duplicated DILexicalBlock(s)
} }
// Builder represents a builder for creating instructions in a function. // Builder represents a builder for creating instructions in a function.

View File

@@ -109,6 +109,8 @@ func (p goTypes) cvtType(typ types.Type) (raw types.Type, cvt bool) {
if elem, cvt := p.cvtType(t.Elem()); cvt { if elem, cvt := p.cvtType(t.Elem()); cvt {
return types.NewChan(t.Dir(), elem), true return types.NewChan(t.Dir(), elem), true
} }
case *types.Tuple:
return p.cvtTuple(t)
default: default:
panic(fmt.Sprintf("cvtType: unexpected type - %T", typ)) panic(fmt.Sprintf("cvtType: unexpected type - %T", typ))
} }