llvm icon indicating copy to clipboard operation
llvm copied to clipboard

`sizeof`?

Open Nv7-GitHub opened this issue 4 years ago • 4 comments

How do you call the sizeof function from LLVM? When C code using sizeof is generated, the code generated just has a constant. Is there a way to find the size of an object from LLVM?

Nv7-GitHub avatar Jun 14 '21 17:06 Nv7-GitHub

Hi @Nv7-GitHub,

There is an initial implementation for handling basic types (e.g. int, float, etc). See irutil.Layout and irutil.DefaultLayout.SizeOf. Note that the implementation does not yet handle calculating the size of composite types (e.g. arrays, structs, etc) as they depend on padding of the memory layout.

The intention is to parse LLVM data layout strings to determine the handling of padding in structs, arrays, etc. This would be used to implement the irutil.Layout interface and have it determine the size of composite types. This is tracked by issue #189.

For added background, see #66 for further background.

Cheers, Robin

Edit: @Nv7-GitHub should you feel like getting more involved with the llir/llvm project, consider implementing a parser for the LLVM data layout string. This would go a long way to adding support for determining the size of composite types (as covered by issue #189).

mewmew avatar Jun 14 '21 21:06 mewmew

Are there any examples on using irutil.Layout?

Nv7-GitHub avatar Jun 14 '21 22:06 Nv7-GitHub

Are there any examples on using irutil.Layout?

Here is a rough usage example. It assumes 1-byte memory alignment. Preferably such information should be parsed from the LLVM IR data layout string (i.e. llir/llvm/ir.Module.DataLayout).

package main

import (
	"fmt"

	"github.com/llir/irutil"
	"github.com/llir/llvm/ir/types"
)

func main() {
	foo()
	// Output:
	//
	// type: i8 (size: 8 bits)
	// panic: support for size of on type *types.ArrayType not yet implemented

	bar()
	// Output:
	//
	// type: i8 (size: 8 bits)
	// type: [123 x i8] (size: 984 bits)
}

func foo() {
	defer func() {
		e := recover() // recover from panic "support for size of on type *types.ArrayType not yet implemented"
		if e != nil {
			fmt.Printf("recovered from panic: %v\n", e)
		}
	}()

	layout := irutil.DefaultLayout{}

	i8Type := types.I8
	i8Size := layout.SizeOf(i8Type)
	fmt.Printf("type: %v (size: %d bits)\n", i8Type, i8Size)
	// Output:
	//
	// type: i8 (size: 8 bits)

	arrayType := types.NewArray(123, i8Type)
	arraySize := layout.SizeOf(arrayType)
	fmt.Printf("type: %v (size: %d bits)\n", arrayType, arraySize)
	// Output:
	//
	// panic: support for size of on type *types.ArrayType not yet implemented
}

// Layout specifies how data is to be laid out in memory, using one-byte memory
// alignment for padding of struct fields and array elements.
type Layout struct {
	irutil.DefaultLayout
}

// SizeOf returns the size of the given type in number of bits.
func (l Layout) SizeOf(typ types.Type) int {
	const pointerSize = 8 // assume 8 byte pointer size
	switch typ := typ.(type) {
	case *types.VoidType:
		return 0
	case *types.FuncType:
		return pointerSize // assume function types are represented as pointers.
	case *types.MMXType:
		return 64 // MMX registers are 64 bits in size.
	case *types.PointerType:
		return pointerSize
	case *types.VectorType:
		return l.SizeOf(typ.ElemType) * int(typ.Len)
	case *types.LabelType:
		return 0 // TODO: figure out how to handle size of on label types.
	case *types.TokenType:
		return 0 // TODO: figure out how to handle size of on token types.
	case *types.MetadataType:
		return 0 // TODO: figure out how to handle size of on metadata types.
	case *types.ArrayType:
		return l.SizeOf(typ.ElemType) * int(typ.Len)
	case *types.StructType:
		total := 0
		// TODO: figure out how to handle Opaque struct types.
		// TODO: handle padding between struct fields if using other memory
		// alignment than 1 byte.
		align := 1 // TODO: read alignment from data layout of LLVM IR module.
		if typ.Packed {
			align = 1
		}
		for _, field := range typ.Fields {
			fieldSize := l.SizeOf(field)
			_ = align // TODO: handle alignment in between fields.
			total += fieldSize
		}
		return total
	}
	//case *types.IntType:   // handled by irutil.DefaultLayout
	//case *types.FloatType: // handled by irutil.DefaultLayout
	return l.DefaultLayout.SizeOf(typ)
}

func bar() {
	layout := Layout{}

	i8Type := types.I8
	i8Size := layout.SizeOf(i8Type)
	fmt.Printf("type: %v (size: %d bits)\n", i8Type, i8Size)
	// Output:
	//
	// type: i8 (size: 8 bits)

	arrayType := types.NewArray(123, i8Type)
	arraySize := layout.SizeOf(arrayType)
	fmt.Printf("type: %v (size: %d bits)\n", arrayType, arraySize)
	// Output:
	//
	// type: [123 x i8] (size: 984 bits)
}

mewmew avatar Jun 14 '21 22:06 mewmew

@mewmew Probably we can merge these howto to document?

dannypsnl avatar Jul 16 '21 03:07 dannypsnl