Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/powerbi/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Compiler construction 如何在LLVM中实现面向对象的动态调度?_Compiler Construction_Llvm_Llvm Ir_Dynamic Dispatch - Fatal编程技术网

Compiler construction 如何在LLVM中实现面向对象的动态调度?

Compiler construction 如何在LLVM中实现面向对象的动态调度?,compiler-construction,llvm,llvm-ir,dynamic-dispatch,Compiler Construction,Llvm,Llvm Ir,Dynamic Dispatch,我正在尝试为支持动态分派和简单继承的面向对象语言制作一个玩具编译器。这意味着任何扩展父类的子类都可以在声明父类时使用。它继承其所有字段和方法,并且可以重写方法 到目前为止,我一直在考虑实现虚拟函数表,并确保父类和子类的内存布局尽可能相似。下面是一个C语言的小例子来说明我的意思: #include <stdlib.h> typedef struct { struct ParentVTable *_vtable; int inheritedField; } Parent

我正在尝试为支持动态分派和简单继承的面向对象语言制作一个玩具编译器。这意味着任何扩展父类的子类都可以在声明父类时使用。它继承其所有字段和方法,并且可以重写方法

到目前为止,我一直在考虑实现虚拟函数表,并确保父类和子类的内存布局尽可能相似。下面是一个C语言的小例子来说明我的意思:

#include <stdlib.h>

typedef struct {
    struct ParentVTable *_vtable;
    int inheritedField;
} Parent;

struct ParentVTable {
    void (*inheritedMethod)(Parent *, int);
};

void Parent_inheritedMethod(Parent *self, int b) {
    b = 0;
}

struct ParentVTable ParentVTable_inst = {
    .inheritedMethod = &Parent_inheritedMethod
};

void Parent_init(Parent *self) {
    self->_vtable = &ParentVTable_inst;
    self->inheritedField = 42;
}

Parent *Parent_new(void) {
    Parent *self = (Parent*)malloc(sizeof(Parent));
    Parent_init(self);
    return self;
}

typedef struct {
    struct ChildVTable *_vtable;
    int inheritedField;
    int newField;
} Child;

struct ChildVTable {
    void (*inheritedMethod)(Child *, int);
    int (*newMethod)(Child *, int);
};

int Child_newMethod(Child *self, int i) {
    return i + self->inheritedField;
}

struct ChildVTable ChildVTable_inst = {
    .inheritedMethod = (void (*)(Child *, int)) Parent_inheritedMethod,
    .newMethod = &Child_newMethod
};

void Child_init(Child *self) {
    Parent_init((Parent *) self);
    self->_vtable = &ChildVTable_inst;
    self->newField = 0;
}

Child *Child_new(void) {
    Child *self = (Child*)malloc(sizeof(Child));
    Child_init(self);
    return self;
}

int main() {
    Parent *p = (Parent*) Child_new();
    return 0;
}

到目前为止一切都很好。问题是,在我试图制作的语言中,以及在大多数实现继承的面向对象语言中,用C进行的转换是隐式的。在编译时,我无法看到需要转换。因此,我的问题是,我如何知道何时必须对llvm变量进行比特转换,例如在分配它之前,或者在将它作为参数传递给调用之前等等。假设这只在运行时已知。我是否遗漏了什么,我是否应该每次都进行比特广播以确保在代码中传递正确的“对象”?任何帮助都将不胜感激

你看到的是药物的副作用。根据Liskov的原理,面向对象语言有那些隐式转换,LLVM IR是一种汇编语言,而不是,您正在编写一种工具,将代码从一种语言转换为另一种语言,因此必须添加显式转换

编写像and这样的函数,并在必要时调用它们,您会发现它非常自然地工作


原语类型也会发生同样的情况,顺便说一句。
a=b
要求您检查a的类型是否可从b分配,并添加强制转换,强制转换的类型取决于您的语言的类型系统。(在某些语言中,将42强制转换为布尔值会产生true,在另一些语言中会产生false,在某些语言中会导致错误。)在实现所有类型规则时,cast()的实现可能相当复杂。

您看到的是。根据Liskov的原理,面向对象语言有那些隐式转换,LLVM IR是一种汇编语言,而不是,您正在编写一种工具,将代码从一种语言转换为另一种语言,因此必须添加显式转换

编写像and这样的函数,并在必要时调用它们,您会发现它非常自然地工作


原语类型也会发生同样的情况,顺便说一句。
a=b
要求您检查a的类型是否可从b分配,并添加强制转换,强制转换的类型取决于您的语言的类型系统。(在某些语言中,将42强制转换为布尔值会产生true,在另一些语言中会产生false,在某些语言中会导致错误。)当您实现所有类型规则时,cast()的实现可能相当复杂。

因此,例如,对于没有任何类型检查的语言,如JS或Python,每个赋值都强制转换,传递给函数的每个参数都是强制转换的吗?例如,你可以进行某种类型的推断,或者他们只是将所有内容都当作字节来处理。在我的例子中,我进行静态类型检查,并修改了规则,以便您所说的有意义。但我只是想知道…@Desperados在动态类型语言中,所有值通常都具有相同的类型(通常是指向包含动态类型信息的某个对象结构的指针)。至少在超前编译器(对于动态类型语言来说非常罕见)和解释器中是这样。在JIT编译器中,您通常知道具体的类型,因此可以为特定类型生成代码。如果说JS或Python没有类型检查,那就不准确了。它们确实有类型检查,但它发生在运行时而不是编译时(有JIT的情况除外)。在实践中,这意味着这些语言的本机代码的提前编译器必须在无法证明不需要的地方插入类型检查。JS和python确实有一些类型检查,并且这种语言的编译时cast()经常会发出复杂的运行时代码。尽管并非总是如此-即使是使用LLVM的简单python编译器也可能在编译时处理某些强制转换,例如a=“1234”+567,其中+的两个操作数在编译时都是已知的,因此+可以折叠为常量。这不同于java这样的语言,在java这样的语言中,cast()很少发出复杂的代码,但也类似,因为java编译器有时也会发出复杂的运行时检查。例如,对于没有任何类型检查的语言,如JS或Python,每个赋值都是强制转换的,传递给函数的每个参数都是强制转换的吗?例如,你可以进行某种类型的推断,或者他们只是将所有内容都当作字节来处理。在我的例子中,我进行静态类型检查,并修改了规则,以便您所说的有意义。但我只是想知道…@Desperados在动态类型语言中,所有值通常都具有相同的类型(通常是指向包含动态类型信息的某个对象结构的指针)。至少在超前编译器(对于动态类型语言来说非常罕见)和解释器中是这样。在JIT编译器中,您通常知道具体的类型,因此可以为特定类型生成代码。如果说JS或Python没有类型检查,那就不准确了。它们确实有类型检查,但它发生在运行时而不是编译时(有JIT的情况除外)。在实践中,这意味着这些语言的本机代码的提前编译器必须在无法证明不需要的地方插入类型检查。JS和python确实有一些类型检查,并且这种语言的编译时cast()经常会发出复杂的运行时代码。尽管并非总是如此——即使是使用LLVM的简单python编译器也可能在编译时处理一些强制转换
define i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca %struct.Parent*, align 8
  store i32 0, i32* %1, align 4
  %3 = call %struct.Child* @Child_new()
  %4 = bitcast %struct.Child* %3 to %struct.Parent*
  store %struct.Parent* %4, %struct.Parent** %2, align 8
  ret i32 0
}