Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ppci/binutils/linker.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ def add_missing_symbols_from_libraries(self, libraries):
for library in libraries:
self.logger.debug("scanning library for symbols %s", library)
for obj in library:
has_sym = any(map(obj.has_symbol, undefined_symbols))
has_sym = any(map(obj.is_defined_global, undefined_symbols))
if has_sym:
self.logger.debug(
"Using object file %s from library", obj
Expand Down
10 changes: 10 additions & 0 deletions ppci/binutils/objectfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ def defined(self):
def is_global(self):
return self.binding == "global"

@property
def is_defined_global(self):
""" Test if the symbol in defined and global. """
return self.value is not None and self.binding == "global"

@property
def is_function(self):
""" Test if this symbol is a function. """
Expand Down Expand Up @@ -250,6 +255,11 @@ def has_symbol(self, name):
""" Check if this object file has a symbol with name 'name' """
return name in self.symbol_map

def is_defined_global(self, name):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rename this to defines_global

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's more consistent.

""" Test if the symbol "name" is defined and global in "self" """
sym = self.symbol_map.get(name)
return sym and sym.is_defined_global

def get_symbol(self, name):
""" Get a symbol """
return self.symbol_map[name]
Expand Down
50 changes: 25 additions & 25 deletions ppci/lang/c/codegenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ def __init__(self, context):
BasicType.LONGDOUBLE: (ir.f64, 8), # TODO: is this correct?
}
self._constant_evaluator = LinkTimeExpressionEvaluator(self)
# Get strongest alignment constraint to align args in vararg block
# TODO should also take "double" alignment if defined for the target
# architecture for now, no easy way to get it
self.va_alignment = max(
self.context.arch_info.get_alignment("long"),
self.context.arch_info.get_alignment("ptr"),
)

def get_label_block(self, name):
""" Get the ir block for a given label, and create it if necessary """
Expand Down Expand Up @@ -1451,39 +1458,31 @@ def gen_fill_varargs(self, var_args):
"""
if var_args:
# Allocate a memory slab:
size = 0
alignment = 1
for va in var_args:
va_size, va_alignment = self.data_layout(va.typ)
# If not aligned, make it happen:
size += required_padding(size, va_alignment)
size += va_size
alignment = max(alignment, va_alignment)
# each argument is stored in a slot, aligned on the strongest alignment
size = self.va_alignment * len(var_args)
alignment = self.va_alignment
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about structs larger than the va_alignment size, is this allowed? Or can only basic types be passed via vararg way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no type restrictions in the C standard, although I have never seen composite types passed to vararg functions.
Struct/union are possible since they support argument-passing by value, and assignment.

In function calls; we can copy their value starting at one slot and occupying several slots. After the copy we realign the vararg pointer to the beginning of next slot.
In va_arg macro, we use assignment to get the value, we increment the vararg pointer by the type size + the required padding to realign the vararg pointer to the beginning of next slot.


vararg_alloc = self.emit(ir.Alloc("varargs", size, alignment))
vararg_ptr = self.emit(ir.AddressOf(vararg_alloc, "vaptr"))
vararg_ptr2 = vararg_ptr

offset = 0
arg_count = len(var_args)
for argument in var_args:
value = self.gen_expr(argument, rvalue=True)
va_size, va_alignment = self.data_layout(argument.typ)

# handle alignment:
padding = required_padding(offset, va_alignment)
if padding > 0:
offset += padding
vararg_ptr2 = self.builder.emit_add(
vararg_ptr2, padding, ir.ptr
)

# Store value:
self.emit(ir.Store(value, vararg_ptr2))

# Increase pointer:
offset += va_size
vararg_ptr2 = self.builder.emit_add(
vararg_ptr2, va_size, ir.ptr
)
# Update remaining number of arguments
arg_count -= 1

# Increase pointer to next slot if necessary:
if arg_count:
offset += alignment
vararg_ptr2 = self.builder.emit_add(
vararg_ptr2, alignment, ir.ptr
)
else:
# Emit a null pointer when no arguments given:
vararg_ptr = self.emit(ir.Const(0, "varargs", ir.ptr))
Expand Down Expand Up @@ -1554,13 +1553,14 @@ def gen_va_start(self, expr: expressions.BuiltInVaStart):

def gen_va_arg(self, expr: expressions.BuiltInVaArg):
""" Generate code for a va_arg operation """
# TODO: how to deal with proper alignment?
# all arguments are all in similar slots
valist_ptrptr = self.gen_expr(expr.arg_pointer, rvalue=False)
va_ptr = self.emit(ir.Load(valist_ptrptr, "va_ptr", ir.ptr))
ir_typ = self.get_ir_type(expr.typ)
# Load the variable argument:
ir_typ = self.get_ir_type(expr.typ)
value = self.emit(ir.Load(va_ptr, "va_arg", ir_typ))
size = self.emit(ir.Const(self.sizeof(expr.typ), "size", ir.ptr))
# Increment pointer to next slot
size = self.emit(ir.Const(self.va_alignment, "size", ir.ptr))
va_ptr = self.emit(ir.add(va_ptr, size, "incptr", ir.ptr))
self.emit(ir.Store(va_ptr, valist_ptrptr))
return value
Expand Down
13 changes: 10 additions & 3 deletions ppci/lang/c/eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,13 @@ def eval_cast(self, expr):

def eval_unop(self, expr):
""" Evaluate unary operation. """
if expr.op in ["-"]:
# types must have been checked while parsing
if expr.op in ["-", "~"]:
a = self.eval_expr(expr.a)
op_map = {"-": lambda x: -x}
op_map = {
"-": lambda x: -x,
"~": lambda x: ~x,
}
value = op_map[expr.op](a)
elif expr.op == "&":
value = self.eval_take_address(expr.a)
Expand All @@ -108,10 +112,13 @@ def eval_binop(self, expr):
}

# Ensure division is integer division:
if expr.typ.is_integer:
if expr.typ.is_integer_or_enum:
op_map["/"] = lambda x, y: x // y
op_map[">>"] = lambda x, y: x >> y
op_map["<<"] = lambda x, y: x << y
op_map["|"] = lambda x, y: x | y
op_map["&"] = lambda x, y: x & y
op_map["^"] = lambda x, y: x ^ y
else:
op_map["/"] = lambda x, y: x / y

Expand Down
5 changes: 5 additions & 0 deletions ppci/lang/c/nodes/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def is_integer(typ):
""" Test if the given type is of integer type """
return (
isinstance(typ, BasicType) and typ.type_id in BasicType.INTEGER_TYPES
or isinstance(typ, EnumType)
)


Expand Down Expand Up @@ -139,6 +140,10 @@ def is_integer(self):
def is_integer_or_enum(self):
return is_integer(self) or is_enum(self)

@property
def is_arithmetic(self):
return is_integer(self) or is_enum(self) or is_double(self) or is_float(self)

@property
def is_promotable(self):
return is_promotable(self)
Expand Down
10 changes: 8 additions & 2 deletions ppci/lang/c/semantics.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ def on_variable_declaration(
self, storage_class, typ, name, modifiers, location
):
""" Given a declaration, and a declarator, create the proper object """
# TSF trace
print("Found %s" % name)
typ = self.apply_type_modifiers(modifiers, typ)
if isinstance(typ, types.FunctionType):
declaration = self.on_function_declaration(
Expand Down Expand Up @@ -660,6 +662,8 @@ def on_number(self, value, location):
if isinstance(value, int):
if type_specifiers:
typ = self.get_type(type_specifiers)
# TSF trace
print("number type = %s" % typ)
else:
# Use larger type to fit the value if required:
# Try unsigned long,
Expand Down Expand Up @@ -688,6 +692,8 @@ def on_number(self, value, location):
# Check limits of integer
# Note, an integer is always positive
max_value = self.context.limit_max(typ)
# TSF trace
print("max=%s cur=%s" % (max_value, value))
if value > max_value:
self.error(
"Integer value too big for type ({})".format(max_value),
Expand Down Expand Up @@ -865,8 +871,8 @@ def on_unop(self, op, a, location):
if not a.lvalue:
self.error("Expected lvalue", a.location)

if not (a.typ.is_integer or a.typ.is_pointer):
self.error("Expected integer or pointer", a.location)
if not (a.typ.is_arithmetic or a.typ.is_pointer):
self.error("Expected arithmetic or pointer type", a.location)

expr = expressions.UnaryOperator(op, a, a.typ, False, location)
elif op == "-":
Expand Down