· Blog  · 28 min read

一种优秀的虚拟机内存架构 - AQ

虚拟机内存架构直接影响虚拟机的性能和占用。设计一个优秀的架构可以有效提升性能和效率。本文将介绍AQ虚拟机使用的内存架构。

虚拟机内存架构直接影响虚拟机的性能和占用。设计一个优秀的架构可以有效提升性能和效率。本文将介绍AQ虚拟机使用的内存架构。

简介

虚拟机内存架构直接影响虚拟机的性能和占用。设计一个优秀的架构可以有效提升性能和效率。
本文将介绍AQ虚拟机使用的内存架构,以及AQ虚拟机内存的详细标准。
通过对于虚拟机内存架构的优化,有助于虚拟机运行效率减少占用。如果可以,应该尽可能地平衡两者,使虚拟机达到最佳状态。

在某些情况下,应该根据虚拟机的特殊需求进行不同的开发。
例如:在单片机内存受限情况下,需要尽可能地减少占用
而在并行计算性能敏感情况,则需要侧重于性能优化

设计思路

内存架构

基础内存架构

AQ采取了寄存器的基础内存架构,但与标准的寄存器架构有所不同,对寄存器架构进行了部分改进和优化。

此处的寄存器并非CPU中的寄存器,而是在内存中模拟出的虚拟寄存器

选择寄存器的原因

相较与JAVAPython等主流语言虚拟机采取堆栈架构不同,AQ决定采取寄存器架构的原因是性能的优化与字节码的容易理解。
虽然堆栈架构被普遍认为更容易移植和编写,但在实际的性能中会有一些损耗,对于内存的多次访问会减缓其速度,这是不可避免并且难以彻底优化的。因此,为了解决此处的性能损耗AQ采用了寄存器架构。同时,从字节码的角度上说,寄存器架构的字节码更容易理解,其指令类似于函数参数方式,而不是直接面对堆栈的众多操作。

寄存器架构的区别

标准的寄存器架构

标准的寄存器架构中,寄存器包含:

  1. 数据类型 - 寄存器将存储的数据的类型(如int、float、double等)
  2. 数据 - 寄存器将存储的数据的值
  3. (可选)标记 - 寄存器将存储的数据的标记(如变量、函数、类等)
  4. (可选)引用 - 寄存器将存储的数据的引用(如对象的地址等)

尽管不同语言的虚拟机内存架构可能有所不同,但大致都存储了这些信息。

而在AQ开发过程中曾使用了该架构,但是经过测试,其存在较大的内存占用。

以下是AQ曾使用的register.h代码:

// Copyright 2024 AQ authors, All Rights Reserved.
// This program is licensed under the AQ License. You can find the AQ license in
// the root directory.

#ifndef AQ_AQVM_MEMORY_REGISTER_H_
#define AQ_AQVM_MEMORY_REGISTER_H_

#include <stdbool.h>

enum AqvmMemoryRegister_ValueType {
  // TODO(Register): Waiting for the improvement of the register.
  AqvmMemoryRegisterValueType_INT,
  AqvmMemoryRegisterValueType_CONSTINT,
  AqvmMemoryRegisterValueType_FLOAT,
  AqvmMemoryRegisterValueType_CONSTFLOAT,
  AqvmMemoryRegisterValueType_DOUBLE,
  AqvmMemoryRegisterValueType_CONSTDOUBLE,
  AqvmMemoryRegisterValueType_LONG,
  AqvmMemoryRegisterValueType_CONSTLONG,
  AqvmMemoryRegisterValueType_CHARACTER,
  AqvmMemoryRegisterValueType_CONSTCHARACTER,
  AqvmMemoryRegisterValueType_BOOLEAN,
  AqvmMemoryRegisterValueType_CONSTBOOLEAN
};

union AqvmMemoryRegister_Value {
  // TODO(Register): Waiting for the improvement of the register.
  int int_value;
  const int const_int_value;
  float float_value;
  const float const_float_value;
  double double_value;
  const double const_double_value;
  long long_value;
  const long const_long_value;
  char character_value;
  const char const_character_value;
  bool boolean_value;
  const bool const_boolean_value;
};

struct AqvmMemoryRegister_Register {
  enum AqvmMemoryRegister_ValueType type;
  union AqvmMemoryRegister_Value value;
};

#endif

从上述代码可以看出,即使仅保留了必要内容,但由于enum类型的AqvmMemoryRegister_ValueType占用4字节,union类型的AqvmMemoryRegister_Value占用8字节,struct类型本身就会占用12字节内存。

同时,由于C编译器的优化,struct类型的AqvmMemoryRegister_Registerenum类型的type为与union类型的value进行内存对齐,因此加入4字节的填充内存。使struct类型的AqvmMemoryRegister_Register占用16字节。

其中如果使用int等非8字节类型,则会有4字节的填充内存被浪费,从而造成内存损耗。因此在全部的寄存器中会有4-8字节的内存浪费。

AQ的寄存器架构

为了解决传统寄存器架构的占用问题,AQ结合了JVM栈帧局部变量表特点,对内存架构进行了优化,使占用问题显著减少。

以下是备选的三种方案:

// plan 1:
struct AqvmMemoryRegister_Register{
  uint8_t type;
  void* value_ptr;
};
void* value;
AqvmMemoryRegister_Register array[];

// plan 2:
void* value;
// value to the memory address of index 0 is int, the index 0 to the index 1 is
// float, etc.
size_t type[];

// plan 3:
struct AqvmMemoryRegister_Register {
  uint32_t* value;
  size_t size;
};

由于指针占用4-8字节,数据本身占用1-8字节,加上类型1字节,因此plan 1占用6-17字节,同时可能会存在内存对齐,因此plan 1同样会造成极大的内存损失。
事实上,在要求保留内存类型信息时,内存利用率最高的是plan 2,但plan 2不能保存在同一数据结构(如:结构体)中不同类型数据的连贯性,可能会使部分指针操作失效。因此为了内存安全,不使用plan 2
在某些情况下(虚拟机指令集包括类型),plan 3也可以满足内存存储的需要,但由于精简指令集的需要,没有在指令中包含类型信息,因此无法满足虚拟机运行需要。

因此我们采取如下设计,保证对于内存利用率,同时使内存占用问题有了很大改善。

AQ内存直接使用void*指针存储数据,size_t存储占用内存大小,并且使用uint8_t数组存储类型。由于uint8_t占用8位,为减少占用,每个字节使用4位来存储类型。因此,一个uint8_t变量可以存储2个类型。每个uint8_t变量的前4位用于偶数字节的类型,后4位用于奇数字节的类型。

// The struct stores information about the memory.
// |type| is a pointer to an array that stores the type of each byte in the
// memory. Each byte uses 4 bits to store the type. So a uint8_t variable can
// store 2 types. Each uint8_t variable's first 4 bits are used for the even
// byte's type and the next 4 bits are used for the odd byte's type. The type
// list is in types.h.
// |data| is a pointer of type void* to the memory that stores the data.
// |size| is the size of the memory.
// NOTICE: The struct AqvmMemory_Memory only stores information of the memory.
// The memory is allocated by the bytecode function when storing the bytecode.
// The memory of |memory| and |type| is part of the bytecode memory.
struct AqvmMemory_Memory {
  uint8_t* type;
  void* data;
  size_t size;
};

由于内存的原因,对于type的存取需要精确的利用。uint8_t类型需要8位,但是超过了类型的存储需要,因此4位既可以满足对于类型的存储需要,同时又可以减少内存占用。但是需要特殊的函数维持type的存取。

// Sets the type of the data at |index| bytes in |memory| to |type|. |type|
// should be less than 4 bits.
// Returns 0 if successful. Returns -1 if the memory pointer is NULL. Returns -2
// if the type pointer is NULL. Returns -3 if the index is out of range. Returns
// -4 if the type is out of range.
int AqvmMemory_SetType(const struct AqvmMemory_Memory* memory, size_t index,
                       uint8_t type) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_NullMemoryPointer\"",
                              "\"The memory pointer is NULL.\"", NULL);
    return -1;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_NullTypePointer\"",
                              "\"The type pointer is NULL.\"", NULL);
    return -2;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_OutOfMemoryRange\"",
                              "\"The index is out of memory range.\"", NULL);
    return -3;
  }
  if (type > 0x0F) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_OutOfTypeRange\"",
                              "\"The type is out of range.\"", NULL);
    return -4;
  }

  // Sets the type of the data at |index| bytes in memory.
  // Since Aqvm stores type data occupying 4 bits and uint8_t occupying 8 bits,
  // each uint8_t type location stores two type data. The storage locations
  // (high 4 bits, low 4 bits) are set according to the parity of |index|. Even
  // numbers are stored in the high bits of (|index| / 2) and odd numbers are
  // stored in the low bits of (|index| / 2).
  if (index % 2 != 0) {
    memory->type[index / 2] = (memory->type[index / 2] & 0xF0) | type;
  } else {
    memory->type[index / 2] = (memory->type[index / 2] & 0x0F) | (type << 4);
  }

  return 0;
}

// Gets the type of the data at |index| bytes in |memory|.
// Returns the type that is less than 4 bits (0X0F) if successful. Returns 0x11
// if the memory pointer is NULL. Returns 0x12 if the type pointer is NULL.
// Returns 0x13 if the index is out of memory range.
uint8_t AqvmMemory_GetType(const struct AqvmMemory_Memory* memory,
                           size_t index) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_NullMemoryPointer",
                              "The memory pointer is NULL.", NULL);
    return 0x11;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_NullTypePointer",
                              "The type pointer is NULL.", NULL);
    return 0x12;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_OutOfMemoryRange",
                              "The index is out of memory range.", NULL);
    return 0x13;
  }

  // Gets the type of the data at |index| bytes in memory.
  // Since Aqvm stores type data occupying 4 bits and uint8_t occupying 8 bits,
  // each uint8_t type location stores two type data. The storage locations
  // (high 4 bits, low 4 bits) are set according to the parity of |index|. Even
  // numbers are stored in the high bits of (|index| / 2) and odd numbers are
  // stored in the low bits of (|index| / 2).
  if (index % 2 != 0) {
    return memory->type[index / 2] & 0x0F;
  } else {
    return (memory->type[index / 2] & 0xF0) >> 4;
  }
}

但使用该设计对于数据的存储有较高要求,因为数据的长度不固定,因此需要专门的函数配合内存进行操作。

// Writes the data that |data_ptr| points to of size |size| to the data of at
// |index| bytes in |memory|.
// Returns 0 if successful. Returns -1 if the memory pointer is NULL. Returns -2
// if the type pointer is NULL. Returns -3 if the index is out of range. Returns
// -4 if the data pointer is NULL.
int AqvmMemory_WriteData(struct AqvmMemory_Memory* memory, size_t index,
                         void* data_ptr, size_t size) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullMemoryPointer\"",
                              "\"The memory pointer is NULL.\"", NULL);
    return -1;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullTypePointer\"",
                              "\"The type pointer is NULL.\"", NULL);
    return -2;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_OutOfMemoryRange\"",
                              "\"The index is out of memory range.\"", NULL);
    return -3;
  }
  if (data_ptr == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullDataPointer\"",
                              "\"The data pointer is NULL.\"", NULL);
    return -4;
  }

  // Since void* does not have a specific size, pointer moves need to be
  // converted before moving.
  memcpy((void*)((uintptr_t)memory->data + index), data_ptr, size);

  return 0;
}

除了减少内存使用外,避免内存的二次占用同样重要。因此我们复用字节码内存,将内存数据和类型存储在字节码的内存部分中,利用字节码文件中预先分配的内存(字节码文件中包含内存的数据和类型),实现对于内存的高效利用。
因为如果单独存储两部分,则需要有两部分重复的内存数据和类型,一份在内存部分,而另一份,字节码部分则不会被使用,因此我们采取了复用的方法,减少了因内存数据和类型而造成的内存浪费。
但因此需要特殊的函数实现,同时需要注意内存数据和类型的内存的分配和释放由字节码的相关函数进行管理。

// Creates and initializes the struct AqvmMemory_Memory with |data|, |type|, and
// |size|. The function will allocate a struct AqvmMemory_Memory and copy
// |data|, |type|, and |size| into the struct. Returns a pointer to the struct
// if successful. Returns NULL if creation fails.
struct AqvmMemory_Memory* AqvmMemory_InitializeMemory(void* data, void* type,
                                                      size_t size) {
  AqvmBaseLogging_OutputLog("\"INFO\"", "\"AqvmMemory_InitializeMemory_Start\"",
                            "\"Memory initialization started.\"", NULL);

  struct AqvmMemory_Memory* memory_ptr =
      (struct AqvmMemory_Memory*)malloc(sizeof(struct AqvmMemory_Memory));
  if (memory_ptr == NULL) {
    AqvmBaseLogging_OutputLog(
        "\"ERROR\"", "\"AqvmMemory_InitializeMemory_MemoryAllocationFailure\"",
        "\"Failed to allocate memory.\"", NULL);
    return NULL;
  }

  memory_ptr->data = data;
  memory_ptr->type = type;
  memory_ptr->size = size;

  return memory_ptr;
}

// Free the memory of the |memory_ptr|. No return.
// NOTICE: The function only free the memory of the struct. The memory pointed
// to by pointers to data and type in struct is not freed. This memory is
// managed by bytecode related functions.
void AqvmMemory_FreeMemory(struct AqvmMemory_Memory* memory_ptr) {
  AqvmBaseLogging_OutputLog("\"INFO\"", "\"AqvmMemory_FreeMemory_Start\"",
                            "\"Memory deallocation started.\"", NULL);

  free(memory_ptr);
}

除此之外,由于部分系统对于类型的定义与AQ标准有所差异,因此设计了相关函数确保虚拟机符合标准。如果系统与标准存在差异,应当为这些系统进行特殊的设计。

// Checks the memory conditions in the system.
// Returns the number of warnings.
int AqvmMemory_CheckMemoryConditions() {
  int warning_count = 0;
  if (sizeof(aqbyte) != 1) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_ByteLengthWarning\"",
        "\"The length requirement for the byte type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqint) != 4) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_IntLengthWarning\"",
        "\"The length requirement for the int type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqlong) != 8) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_LongLengthWarning\"",
        "\"The length requirement for the long type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqfloat) != 4) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"",
        "\"AqvmMemory_CheckMemoryConditions_FloatLengthWarning\"",
        "\"The length requirement for the float type does not conform to the "
        "type definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqdouble) != 8) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"",
        "\"AqvmMemory_CheckMemoryConditions_DoubleLengthWarning\"",
        "\"The length requirement for the double type does not conform to the "
        "type definition.\"",
        NULL);
    ++warning_count;
  }

  if (warning_count == 0) {
    AqvmBaseLogging_OutputLog(
        "\"INFO\"", "\"AqvmMemory_CheckMemoryConditions_CheckNormal\"",
        "\"No memory conditions warning.\"", NULL);
  }

  return warning_count;
}

详细标准:

目录结构

memory部分的代码位于/aqvm/memory。内含多个代码文件。

  1. CMakeLists.txt - 该目录下的CMake构建文件
  2. memory.h - 内存的数据结构和相关函数
  3. memory.c - 内存的相关函数的实现
  4. types.h - 内存类型的定义

memory.h

AqvmMemory_Memory

该结构体存储有关内存的信息。
|type| 是一个指向数组的指针,该数组存储内存中每个字节的类型。每个字节使用4位来存储类型。因此,一个 uint8_t 变量可以存储2个类型。每个 uint8_t 变量的前4位用于偶数字节的类型,后4位用于奇数字节的类型。类型列表在 types.h 中。
|data| 是一个指向存储数据的内存的 void* 类型的指针。
|size| 是内存的大小。
注意:结构体 AqvmMemory_Memory 仅存储内存的信息。内存由存储字节码时的字节码函数分配。|memory| 和 |type| 的内存是字节码内存的一部分。

struct AqvmMemory_Memory {
  uint8_t* type;
  void* data;
  size_t size;
};

AqvmMemory_CheckMemoryConditions

检查系统中的内存条件。
返回警告数量。

int AqvmMemory_CheckMemoryConditions() {
  int warning_count = 0;
  if (sizeof(aqbyte) != 1) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_ByteLengthWarning\"",
        "\"The length requirement for the byte type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqint) != 4) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_IntLengthWarning\"",
        "\"The length requirement for the int type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqlong) != 8) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_LongLengthWarning\"",
        "\"The length requirement for the long type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqfloat) != 4) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"",
        "\"AqvmMemory_CheckMemoryConditions_FloatLengthWarning\"",
        "\"The length requirement for the float type does not conform to the "
        "type definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqdouble) != 8) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"",
        "\"AqvmMemory_CheckMemoryConditions_DoubleLengthWarning\"",
        "\"The length requirement for the double type does not conform to the "
        "type definition.\"",
        NULL);
    ++warning_count;
  }

  if (warning_count == 0) {
    AqvmBaseLogging_OutputLog(
        "\"INFO\"", "\"AqvmMemory_CheckMemoryConditions_CheckNormal\"",
        "\"No memory conditions warning.\"", NULL);
  }

  return warning_count;
}

AqvmMemory_InitializeMemory

创建包含 |data|、|type| 和 |size| 的结构体 AqvmMemory_Memory。
该函数将分配一个 AqvmMemory_Memory 结构体,并将 |data|、|type| 和 |size| 复制到结构体中。返回指向该结构体的指针。如果创建失败则返回NULL。

struct AqvmMemory_Memory* AqvmMemory_InitializeMemory(void* data, void* type,
                                                      size_t size) {
  AqvmBaseLogging_OutputLog("\"INFO\"", "\"AqvmMemory_InitializeMemory_Start\"",
                            "\"Memory initialization started.\"", NULL);

  struct AqvmMemory_Memory* memory_ptr =
      (struct AqvmMemory_Memory*)malloc(sizeof(struct AqvmMemory_Memory));
  if (memory_ptr == NULL) {
    AqvmBaseLogging_OutputLog(
        "\"ERROR\"", "\"AqvmMemory_InitializeMemory_MemoryAllocationFailure\"",
        "\"Failed to allocate memory.\"", NULL);
    return NULL;
  }

  memory_ptr->data = data;
  memory_ptr->type = type;
  memory_ptr->size = size;

  return memory_ptr;
}

AqvmMemory_FreeMemory

释放 |memory_ptr| 的内存。无返回值。
注意:该函数仅释放结构体的内存。结构体中指向数据和类型的指针所指向的内存不会被释放。这些内存由字节码相关函数管理。

void AqvmMemory_FreeMemory(struct AqvmMemory_Memory* memory_ptr) {
  AqvmBaseLogging_OutputLog("\"INFO\"", "\"AqvmMemory_FreeMemory_Start\"",
                            "\"Memory deallocation started.\"", NULL);

  free(memory_ptr);
}

AqvmMemory_SetType

设置 |memory| 中 |index| 字节处的数据类型为 |type|。|type| 应小于 4 位。
成功时返回 0。如果内存指针为 NULL,返回 -1。如果索引指针为 NULL,返回 -2。如果索引超出范围,返回 -3。如果类型超出范围,返回 -4。

int AqvmMemory_SetType(const struct AqvmMemory_Memory* memory, size_t index,
                       uint8_t type) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_NullMemoryPointer\"",
                              "\"The memory pointer is NULL.\"", NULL);
    return -1;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_NullTypePointer\"",
                              "\"The type pointer is NULL.\"", NULL);
    return -2;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_OutOfMemoryRange\"",
                              "\"The index is out of memory range.\"", NULL);
    return -3;
  }
  if (type > 0x0F) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_OutOfTypeRange\"",
                              "\"The type is out of range.\"", NULL);
    return -4;
  }

  // Sets the type of the data at |index| bytes in memory.
  // Since Aqvm stores type data occupying 4 bits and uint8_t occupying 8 bits,
  // each uint8_t type location stores two type data. The storage locations
  // (high 4 bits, low 4 bits) are set according to the parity of |index|. Even
  // numbers are stored in the high bits of (|index| / 2) and odd numbers are
  // stored in the low bits of (|index| / 2).
  if (index % 2 != 0) {
    memory->type[index / 2] = (memory->type[index / 2] & 0xF0) | type;
  } else {
    memory->type[index / 2] = (memory->type[index / 2] & 0x0F) | (type << 4);
  }

  return 0;
}

AqvmMemory_GetType

获取 |memory| 中 |index| 字节处的数据类型。
成功时返回小于 4 位 (0X0F) 的类型。如果内存指针为 NULL,返回 0x11。如果索引指针为 NULL,返回 0x12。如果索引超出内存范围,返回 0x13。

uint8_t AqvmMemory_GetType(const struct AqvmMemory_Memory* memory,
                           size_t index) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_NullMemoryPointer",
                              "The memory pointer is NULL.", NULL);
    return 0x11;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_NullTypePointer",
                              "The type pointer is NULL.", NULL);
    return 0x12;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_OutOfMemoryRange",
                              "The index is out of memory range.", NULL);
    return 0x13;
  }

  // Gets the type of the data at |index| bytes in memory.
  // Since Aqvm stores type data occupying 4 bits and uint8_t occupying 8 bits,
  // each uint8_t type location stores two type data. The storage locations
  // (high 4 bits, low 4 bits) are set according to the parity of |index|. Even
  // numbers are stored in the high bits of (|index| / 2) and odd numbers are
  // stored in the low bits of (|index| / 2).
  if (index % 2 != 0) {
    return memory->type[index / 2] & 0x0F;
  } else {
    return (memory->type[index / 2] & 0xF0) >> 4;
  }
}

AqvmMemory_WriteData

将 |data_ptr| 指向的大小为 |size| 的数据写入 |memory| 中 |index| 字节处的数据。
成功时返回 0。如果内存指针为 NULL,返回 -1。如果索引指针为 NULL,返回 -2。如果索引超出内存范围,返回 -3。如果数据指针为 NULL,返回 -4。

int AqvmMemory_WriteData(struct AqvmMemory_Memory* memory, size_t index,
                         void* data_ptr, size_t size) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullMemoryPointer\"",
                              "\"The memory pointer is NULL.\"", NULL);
    return -1;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullTypePointer\"",
                              "\"The type pointer is NULL.\"", NULL);
    return -2;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_OutOfMemoryRange\"",
                              "\"The index is out of memory range.\"", NULL);
    return -3;
  }
  if (data_ptr == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullDataPointer\"",
                              "\"The data pointer is NULL.\"", NULL);
    return -4;
  }

  // Since void* does not have a specific size, pointer moves need to be
  // converted before moving.
  memcpy((void*)((uintptr_t)memory->data + index), data_ptr, size);

  return 0;
}

memory.h完整代码:

// Copyright 2024 AQ author, All Rights Reserved.
// This program is licensed under the AQ License. You can find the AQ license in
// the root directory.

#ifndef AQ_AQVM_MEMORY_MEMORY_H_
#define AQ_AQVM_MEMORY_MEMORY_H_

#include <stddef.h>
#include <stdint.h>

#include "aqvm/memory/types.h"

// The struct stores information about the memory.
// |type| is a pointer to an array that stores the type of each byte in the
// memory. Each byte uses 4 bits to store the type. So a uint8_t variable can
// store 2 types. Each uint8_t variable's first 4 bits are used for the even
// byte's type and the next 4 bits are used for the odd byte's type. The type
// list is in types.h.
// |data| is a pointer of type void* to the memory that stores the data.
// |size| is the size of the memory.
// NOTICE: The struct AqvmMemory_Memory only stores information of the memory.
// The memory is allocated by the bytecode function when storing the bytecode.
// The memory of |memory| and |type| is part of the bytecode memory.
struct AqvmMemory_Memory {
  uint8_t* type;
  void* data;
  size_t size;
};

// Checks the memory conditions in the system.
// Returns the number of warnings.
int AqvmMemory_CheckMemoryConditions();

// Creates and initializes the struct AqvmMemory_Memory with |data|, |type|, and
// |size|. The function will allocate a struct AqvmMemory_Memory and copy
// |data|, |type|, and |size| into the struct. Returns a pointer to the struct
// if successful. Returns NULL if creation fails.
struct AqvmMemory_Memory* AqvmMemory_InitializeMemory(void* data, void* type,
                                                      size_t size);

// Free the memory of the |memory_ptr|. No return.
// NOTICE: The function only free the memory of the struct. The memory pointed
// to by pointers to data and type in struct is not freed. This memory is
// managed by bytecode related functions.
void AqvmMemory_FreeMemory(struct AqvmMemory_Memory* memory_ptr);

// Sets the type of the data at |index| bytes in |memory| to |type|. |type|
// should be less than 4 bits.
// Returns 0 if successful. Returns -1 if the memory pointer is NULL. Returns -2
// if the type pointer is NULL. Returns -3 if the index is out of range. Returns
// -4 if the type is out of range.
int AqvmMemory_SetType(const struct AqvmMemory_Memory* memory, size_t index,
                       uint8_t type);

// Gets the type of the data at |index| bytes in |memory|.
// Returns the type that is less than 4 bits (0X0F) if successful. Returns 0x11
// if the memory pointer is NULL. Returns 0x12 if the type pointer is NULL.
// Returns 0x13 if the index is out of memory range.
uint8_t AqvmMemory_GetType(const struct AqvmMemory_Memory* memory,
                           size_t index);

// Writes the data that |data_ptr| points to of size |size| to the data of at
// |index| bytes in |memory|.
// Returns 0 if successful. Returns -1 if the memory pointer is NULL. Returns -2
// if the type pointer is NULL. Returns -3 if the index is out of range. Returns
// -4 if the data pointer is NULL.
int AqvmMemory_WriteData(struct AqvmMemory_Memory* memory, size_t index,
                         void* data_ptr, size_t size);

#endif

memory.c

memory.c完整代码:

// Copyright 2024 AQ author, All Rights Reserved.
// This program is licensed under the AQ License. You can find the AQ license in
// the root directory.

#include "aqvm/memory/memory.h"

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "aqvm/memory/types.h"
#include "aqvm/base/logging/logging.h"

int AqvmMemory_CheckMemoryConditions() {
  int warning_count = 0;
  if (sizeof(aqbyte) != 1) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_ByteLengthWarning\"",
        "\"The length requirement for the byte type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqint) != 4) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_IntLengthWarning\"",
        "\"The length requirement for the int type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqlong) != 8) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"", "\"AqvmMemory_CheckMemoryConditions_LongLengthWarning\"",
        "\"The length requirement for the long type does not conform to the "
        "type "
        "definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqfloat) != 4) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"",
        "\"AqvmMemory_CheckMemoryConditions_FloatLengthWarning\"",
        "\"The length requirement for the float type does not conform to the "
        "type definition.\"",
        NULL);
    ++warning_count;
  }
  if (sizeof(aqdouble) != 8) {
    AqvmBaseLogging_OutputLog(
        "\"WARNING\"",
        "\"AqvmMemory_CheckMemoryConditions_DoubleLengthWarning\"",
        "\"The length requirement for the double type does not conform to the "
        "type definition.\"",
        NULL);
    ++warning_count;
  }

  if (warning_count == 0) {
    AqvmBaseLogging_OutputLog(
        "\"INFO\"", "\"AqvmMemory_CheckMemoryConditions_CheckNormal\"",
        "\"No memory conditions warning.\"", NULL);
  }

  return warning_count;
}

struct AqvmMemory_Memory* AqvmMemory_InitializeMemory(void* data, void* type,
                                                      size_t size) {
  AqvmBaseLogging_OutputLog("\"INFO\"", "\"AqvmMemory_InitializeMemory_Start\"",
                            "\"Memory initialization started.\"", NULL);

  struct AqvmMemory_Memory* memory_ptr =
      (struct AqvmMemory_Memory*)malloc(sizeof(struct AqvmMemory_Memory));
  if (memory_ptr == NULL) {
    AqvmBaseLogging_OutputLog(
        "\"ERROR\"", "\"AqvmMemory_InitializeMemory_MemoryAllocationFailure\"",
        "\"Failed to allocate memory.\"", NULL);
    return NULL;
  }

  memory_ptr->data = data;
  memory_ptr->type = type;
  memory_ptr->size = size;

  return memory_ptr;
}

void AqvmMemory_FreeMemory(struct AqvmMemory_Memory* memory_ptr) {
  AqvmBaseLogging_OutputLog("\"INFO\"", "\"AqvmMemory_FreeMemory_Start\"",
                            "\"Memory deallocation started.\"", NULL);

  free(memory_ptr);
}

int AqvmMemory_SetType(const struct AqvmMemory_Memory* memory, size_t index,
                       uint8_t type) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_NullMemoryPointer\"",
                              "\"The memory pointer is NULL.\"", NULL);
    return -1;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_NullTypePointer\"",
                              "\"The type pointer is NULL.\"", NULL);
    return -2;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_OutOfMemoryRange\"",
                              "\"The index is out of memory range.\"", NULL);
    return -3;
  }
  if (type > 0x0F) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_SetType_OutOfTypeRange\"",
                              "\"The type is out of range.\"", NULL);
    return -4;
  }

  // Sets the type of the data at |index| bytes in memory.
  // Since Aqvm stores type data occupying 4 bits and uint8_t occupying 8 bits,
  // each uint8_t type location stores two type data. The storage locations
  // (high 4 bits, low 4 bits) are set according to the parity of |index|. Even
  // numbers are stored in the high bits of (|index| / 2) and odd numbers are
  // stored in the low bits of (|index| / 2).
  if (index % 2 != 0) {
    memory->type[index / 2] = (memory->type[index / 2] & 0xF0) | type;
  } else {
    memory->type[index / 2] = (memory->type[index / 2] & 0x0F) | (type << 4);
  }

  return 0;
}

uint8_t AqvmMemory_GetType(const struct AqvmMemory_Memory* memory,
                           size_t index) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_NullMemoryPointer",
                              "The memory pointer is NULL.", NULL);
    return 0x11;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_NullTypePointer",
                              "The type pointer is NULL.", NULL);
    return 0x12;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("ERROR", "AqvmMemory_GetType_OutOfMemoryRange",
                              "The index is out of memory range.", NULL);
    return 0x13;
  }

  // Gets the type of the data at |index| bytes in memory.
  // Since Aqvm stores type data occupying 4 bits and uint8_t occupying 8 bits,
  // each uint8_t type location stores two type data. The storage locations
  // (high 4 bits, low 4 bits) are set according to the parity of |index|. Even
  // numbers are stored in the high bits of (|index| / 2) and odd numbers are
  // stored in the low bits of (|index| / 2).
  if (index % 2 != 0) {
    return memory->type[index / 2] & 0x0F;
  } else {
    return (memory->type[index / 2] & 0xF0) >> 4;
  }
}

int AqvmMemory_WriteData(struct AqvmMemory_Memory* memory, size_t index,
                         void* data_ptr, size_t size) {
  if (memory == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullMemoryPointer\"",
                              "\"The memory pointer is NULL.\"", NULL);
    return -1;
  }
  if (memory->type == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullTypePointer\"",
                              "\"The type pointer is NULL.\"", NULL);
    return -2;
  }
  if (index > memory->size) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_OutOfMemoryRange\"",
                              "\"The index is out of memory range.\"", NULL);
    return -3;
  }
  if (data_ptr == NULL) {
    AqvmBaseLogging_OutputLog("\"ERROR\"",
                              "\"AqvmMemory_WriteData_NullDataPointer\"",
                              "\"The data pointer is NULL.\"", NULL);
    return -4;
  }

  // Since void* does not have a specific size, pointer moves need to be
  // converted before moving.
  memcpy((void*)((uintptr_t)memory->data + index), data_ptr, size);

  return 0;
}

通过这些代码的配合,共同构成了完整的Aqvm的内存架构,有效缓解内存压力的同时,提高了Aqvm的运行效率。

我们正在更加努力地开发AQ虚拟机。如果您想了解更多信息或参与开发工作,请关注我们的官网:https://www.axa6.com 和 Github:https://github.com/aq-org/AQ。

本文章基于AQ License:https://github.com/aq-org/AQ/blob/main/LICENSE 发布,如有需要,请根据AQ License进行改编或转载。

Share:
Back to Blog

Related Posts

View All Posts »
AQ虚拟机的类型定义 - AQ

AQ虚拟机的类型定义 - AQ

由于不同的系统、硬件等外部条件对于内存有不同的支持和定义,因此为了使AQ虚拟机满足跨平台运行的要求,设计统一的类型标准是必不可少的。本文对于AQ虚拟机的类型进行了定义和规范,以确保在不同系统上的AQ虚拟机均能正常运行。

如何从零设计一门编程语言? - AQ

如何从零设计一门编程语言? - AQ

如何从零设计一门编程语言?新型的编程语言应该包括那些部分?本文章将通过AQ语言的设计,详述开发一个编程语言的过程,从零开始设计、开发编译器和相关基础设施,最终完成一门编程语言的构建。

关于AQ官方网站多语言支持的公告 - AQ

关于AQ官方网站多语言支持的公告 - AQ

由于AQ的开发工作日益增加,缺少专业翻译人员以及对于翻译效果不理想等原因,即日起,AQ官方网站多语言版本将暂停实时维护。仅保留英语和中文两种语言支持。