LLVM Cpu0 新后端6
想好好熟悉一下llvm开发一个新后端都要干什么,于是参考了老师的系列文章:
LLVM 后端实践笔记
代码在这里(还没来得及准备,先用网盘暂存一下):
链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?pwd=vd6s 提取码: vd6s
之前的章节只实现了 int 和 32 位的 long 类型数据,这一章会新增一些更复杂的数据类型,比如 char, bool, short, long long,还会增加结构体,浮点,和向量类型。这一部分内容相对比较简单,其实这些类型也都是标准语言都支持的类型,所以 LLVM 自身已经实现了很大一部分功能,只要我们的后端不那么奇怪,就很容易填补缺失的内容。
目录
一、修改的文件
1.1 Cpu0ISelDAGToDAG.cpp
1.2 Cpu0ISelLowering.cpp/.h
1.3 Cpu0InstrInfo.td
1.4 Cpu0SEISelDAGToDAG.cpp/.h
1.5 MCTargetDesc/Cpu0InstPrinter.cpp/.h
二、实现结果
2.1 局部指针
2.2 char类型
2.3 bool类型
2.4 short
2.5 long long 类型
2.6 局部数组、结构体
2.7 全局数组、结构体
2.8 向量
2.9 cl指令
一、修改的文件
1.1 Cpu0ISelDAGToDAG.cpp
在SelectAddr接口内增加对于基址+常量偏移这种地址形式的处理,对于全局基址加常量偏移的情况,提取其基址和偏移。。
1.2 Cpu0ISelLowering.cpp/.h
有关于对 bool 类型的处理,这里增加了一些对 i1 类型 Promote 的合法化描述,告诉 LLVM 在遇到对 i1 类型的 extend 时要做 Promote。Promote 是将较小宽度的数据类型扩展成对应的能够支持的更宽的数据宽度类型,在指令选择的类型合法化阶段会起到作用。在 long long 实现中,在 Lowering 的位置还需要增加对 long long 类型的移位操作合法化。
覆盖一个函数 isOffsetFoldingLegal(),直接返回 false,避免带偏移的全局地址展开,Cpu0 和 Mips 一样无法处理这种情况。我们实现的 getAddrNonPIC() 方法中,将全局符号地址展开成一条加法指令,对地址的高低位做加法运算。所以实际上我们会将一条全局地址带偏移的寻址展开成加法运算 base,然后再把结果与 offset 相加的 DAG(在 Cpu0ISelDAGToDAG.cpp 中的 SelectAddr 中提取这种情况的 node Value,此时就已经是两个 add node 了)。
最后,还需要对向量类型的支持做一小部分改动,覆盖 getSetCCResultType() 方法,如果是向量类型,使用 VT.changeVectorElementTypeToInteger() 方法返回 CC 值。
1.3 Cpu0InstrInfo.td
到目前,因为我们添加数据类型的很多实现代码已经在公共 LLVM 代码中实现,所以实际上大多数修改都在 td 文件中。
新增一个 mem_ea 的操作数类型,这是一个 complexpattern,会定义其 encoding 操作和 printinst 等操作,它用来描述指令 pattern 中的地址表示;然后要定义一个 LEA_ADDiu 的模式,这是一个不会输出成指令的模式,它实际上是计算地址+偏移的结果,这和 sparc 处理器中的 LEA_ADDRi 是一样的效果。
新增 i8 和 i16 相关的 extend 类型以及对应的 ld/st,命名为 LB, LBu, SB, LH, LHu, SH。LB, LH 处理有符号的 i8/i16 类型 load,LBu, LHu 处理无符号的 i8/i16 类型 load,SB, SH 处理i8/i16 类型的store。
新增 CountLeading0 和 CountLeading1 的 pattern,用来选择到计算前导 0 和计算前导 1 的指令,llvm 内置了 ctlz 的 node(count leading zero),可以直接把 clz 指令接过去,不过对于 count leading 1 是没有对应的 node 的,不过可以通过先对值取反然后求前导 0 的方式实现前导 1 的计算,即 ctlz (not RC:$rb)。
因为 C 语言没有对求前导 0 和前导 1 的原生语法,所以实际上会使用 builtin 接口来实现,也就是说,在 C 语言描述中,为了实现这种功能,需要调用 __builtin_clz() 函数(ctls 就是先对参数取反再调用 ctlz 的 builtin),因为我们使用了内置的 node,所以这部分是 llvm 帮我们实现了。
1.4 Cpu0SEISelDAGToDAG.cpp/.h
定义了一个 selectAddESubE() 方法,用来处理带进位的加减法运算的指令选择。在 trySelect() 方法中,将对 ISD::SUBE, ISD::ADDE 的情况选择用 selectAddESubE() 来处理。
selectAddESubE() 方法为符合条件的 node 新增了一个操作数节点,该节点会读取状态字中进位是否是 1,并将结果叠加到运算中;在 Cpu032I 处理器中,使用 CMP 指令和 ANDi 指令来获取进位状态,在 Cpu032II 处理器中,则使用 SLT 指令直接判断进位。
另外,还要处理 SMUL_LOHI 和 UMUL_LOHI 节点,这是能够直接返回两个运算结果的节点(高低位)。
1.5 MCTargetDesc/Cpu0InstPrinter.cpp/.h
增加mem_ea 的printinst操作的实现。
二、实现结果
2.1 局部指针
int test_local_pointer() {int b = 3;int *p = &b;return *p;
}
addiu $sp, $sp, -8 # 扩栈addiu $2, $zero, 3 # 将3存到寄存器st $2, 4($sp) # 将其存到栈上addiu $2, $sp, 4 # 读出栈中局部变量的地址st $2, 0($sp) # 将这个地址存到栈上ld $2, 0($sp) # 读出这个地址ld $2, 0($2) # 读出地址里的内容addiu $sp, $sp, 8 # 回栈ret $lr # 返回
2.2 char类型
struct Date
{short year;char month;char day;char hour;char minute;char second;
};unsigned char b[4] = {'a', 'b', 'c', '\0'};int test_char()
{unsigned char a = b[1];char c = (char)b[1];struct Date date1 = {2021, (char)2, (char)27, (char)17, (char)22, (char)10};char m = date1.month;char s = date1.second;return 0;
}
addiu $sp, $sp, -24lui $2, %got_hi(b)addu $2, $2, $gpld $2, %got_lo(b)($2) # 与上边搭配加载全局变量b的首地址lbu $3, 1($2) # 计算b[1]的地址,存到寄存器3sb $3, 20($sp) # 将寄存器3内的地址存到栈上lbu $2, 1($2) # 再次计算b[1]的地址,存到寄存器2sb $2, 16($sp) # 将寄存器2内的地址存到栈上ld $2, %got($__const.test_char.date1)($gp)ori $2, $2, %lo($__const.test_char.date1) # 获取要写入局部变量对象的常量的地址lhu $3, 6($2) # 将偏移6处的内容load到寄存器3中,lhu是i16的,就是load范围是2字节lhu $4, 4($2) # 将偏移4处的内容load到寄存器4中,lhu是i16的,就是load范围是2字节shl $4, $4, 16 or $3, $4, $3 # 这两条是将上述load出来的两个2字节的内容拼成一个4字节的st $3, 12($sp) # 将这4字节存到栈上,也就是存放hour, minute, second到 date1lhu $3, 2($2) # 这里是相同的逻辑lhu $2, 0($2)shl $2, $2, 16or $2, $2, $3st $2, 8($sp) # 将这4字节存到栈上,也就是存放year, month, day到 date1lbu $2, 10($sp) # 从偏移10的位置读出date1.month(我们知道year, month, day在偏移8的位置,year两个字节,因此month在偏移10的位置,这里很正确)sb $2, 4($sp) # 将其存到栈上(m)lbu $2, 14($sp) # 从偏移14的位置读出date1.secondsb $2, 0($sp) # 将其存到栈上(s)addiu $2, $zero, 0addiu $sp, $sp, 24ret $lr
2.3 bool类型
bool test_load_bool()
{int a = 1;if (a < 0)return false;return true;
}
这里涉及到跳转我们当前可能编不过,下一节的内容加上之后我们就可以编过了,我们先提前看一下效果。
_Z14test_load_boolv:
# %bb.0:addiu $sp, $sp, -8addiu $2, $zero, 1st $2, 0($sp)ld $2, 0($sp)addiu $3, $zero, -1slt $2, $3, $2bne $2, $zero, $BB0_2nop
# %bb.1:addiu $2, $zero, 0sb $2, 7($sp) # 使用 sb 将 bool 类型的 0 写入栈jmp $BB0_3
$BB0_2:addiu $2, $zero, 1sb $2, 7($sp) # 使用 sb 将 bool 类型的 1 写入栈
$BB0_3:lbu $2, 7($sp)addiu $sp, $sp, 8ret $lr
2.4 short
int test_signed_char()
{char a = 0x80;int i = (signed int)a;i = i + 2; // i = (-128 + 2) = -126return i;
}int test_unsigned_char()
{unsigned char c = 0x80;unsigned int ui = (unsigned int)c;ui = ui + 2; // ui = (128 + 2) = 130return (int)ui;
}int test_signed_short()
{short a = 0x8000;int i = (signed int)a;i = i + 2; // i = (-32768 + 2) = -32766return i;
}int test_unsigned_short()
{unsigned short c = 0x8000;unsigned int ui = (unsigned int)c;ui = ui + 2; // ui = (32768 + 2) = 32770return (int)ui;
}
st_signed_short
...addiu $sp, $sp, -8ori $2, $zero, 32768sh $2, 4($sp)lh $2, 4($sp)st $2, 0($sp)ld $2, 0($sp)addiu $2, $2, 2st $2, 0($sp)ld $2, 0($sp)addiu $sp, $sp, 8ret $lr
...
test_unsigned_short:
...addiu $sp, $sp, -8ori $2, $zero, 32768sh $2, 4($sp)lhu $2, 4($sp)st $2, 0($sp)ld $2, 0($sp)addiu $2, $2, 2st $2, 0($sp)ld $2, 0($sp)addiu $sp, $sp, 8ret $lr
...
汇编还是很好理解的,这里就不进行详细的分析了。
2.5 long long 类型
long long test_longlong()
{long long a = 0x300000002;long long b = 0x100000001;int a1 = 0x30010000;int b1 = 0x20010000;long long c = a + b; // c = 0x00000004,00000003long long d = a - b; // d = 0x00000002,00000001long long e = a * b; // e = 0x00000005,00000002long long f = (long long)a1 * (long long)b1; // f = 0x00060050,01000000return (c+d+e+f); // (0x0006005b,01000006) = (393307,16777222)
}
addiu $sp, $sp, -56addiu $2, $zero, 2st $2, 52($sp) # a的低位addiu $2, $zero, 3st $2, 48($sp) # a的高位addiu $2, $zero, 1st $2, 44($sp) # b的低位st $2, 40($sp) # b的高位lui $2, 12289st $2, 36($sp) # a1lui $2, 8193st $2, 32($sp) # b1ld $2, 52($sp) # a的低位ld $3, 48($sp) # a的高位ld $4, 44($sp) # b的低位ld $5, 40($sp) # b的高位addu $3, $3, $5 # 高位相加addu $4, $2, $4 # 低位相加sltu $2, $4, $2 # 判断低位加法是否有进位addu $2, $3, $2 # 将进位与高位结果相加st $4, 28($sp) # 下同st $2, 24($sp)ld $2, 48($sp)ld $3, 52($sp)ld $4, 40($sp)ld $5, 44($sp)sltu $6, $3, $5subu $2, $2, $4subu $2, $2, $6subu $3, $3, $5st $3, 20($sp)st $2, 16($sp)ld $2, 48($sp)ld $3, 52($sp)ld $4, 44($sp)ld $5, 40($sp)mul $5, $3, $5multu $3, $4mflo $3mfhi $6addu $5, $6, $5mul $2, $2, $4addu $2, $5, $2st $3, 12($sp)st $2, 8($sp)ld $2, 36($sp)ld $3, 32($sp)mult $2, $3mflo $2mfhi $3st $2, 4($sp)st $3, 0($sp)ld $2, 28($sp)ld $3, 24($sp)ld $4, 20($sp)ld $5, 16($sp)addu $3, $3, $5addu $4, $2, $4sltu $2, $4, $2addu $2, $3, $2ld $3, 8($sp)ld $5, 12($sp)addu $5, $4, $5sltu $4, $5, $4addu $2, $2, $3addu $2, $2, $4ld $3, 4($sp)ld $4, 0($sp)addu $2, $2, $4addu $3, $5, $3sltu $4, $3, $5addu $2, $2, $4addiu $sp, $sp, 56ret $lr
2.6 局部数组、结构体
与2.2中的局部结构体类似。
2.7 全局数组、结构体
struct Date
{int year;int month;int day;
};struct Date date = {2021, 2, 27};
int a[3] = {2021, 2, 27};int test_struct()
{int day = date.day;int i = a[1];return (i+day); // 2 + 27 = 29
}
addiu $sp, $sp, -8lui $2, %got_hi(date)addu $2, $2, $gpld $2, %got_lo(date)($2) # 从got表中取全局变量date的地址ld $2, 8($2) # 从偏移8的地方load出date.day,(year和month各占4字节)st $2, 4($sp) # 存到栈中(day)lui $2, %got_hi(a) # 下同addu $2, $2, $gpld $2, %got_lo(a)($2)ld $2, 4($2)st $2, 0($sp)ld $2, 0($sp)ld $3, 4($sp)addu $2, $2, $3addiu $sp, $sp, 8ret $lr
2.8 向量
typedef long vector8long __attribute__((__vector_size__(32)));
typedef long vector8short __attribute__((__vector_size__(16)));int test_cmplt_short()
{volatile vector8short a0 = {0, 1, 2, 3};volatile vector8short b0 = {2, 2, 2, 2};volatile vector8short c0;c0 = a0 < b0;return (int)(c0[0] + c0[1] + c0[2] + c0[3]);
}int test_cmplt_long()
{volatile vector8long a0 = {2, 2, 2, 2, 1, 1, 1, 1};volatile vector8long b0 = {1, 1, 1, 1, 2, 2, 2, 2};volatile vector8long c0;c0 = a0 < b0;return (c0[0] + c0[1] + c0[2] + c0[3] + c0[4] + c0[5] + c0[6] + c0[7]);
}
下述是test_cmplt_short函数的汇编,我们看个稍微短一点的:
addiu $sp, $sp, -64st $10, 60($sp) # 4-byte Folded Spillst $9, 56($sp) # 4-byte Folded Spilladdiu $2, $zero, 3st $2, 44($sp)addiu $3, $zero, 2st $3, 40($sp)addiu $2, $zero, 1st $2, 36($sp)addiu $2, $zero, 0st $2, 32($sp)st $3, 28($sp)st $3, 24($sp)st $3, 20($sp)st $3, 16($sp)ld $3, 44($sp)ld $4, 40($sp)ld $5, 36($sp)ld $6, 32($sp)ld $7, 28($sp)ld $8, 24($sp)ld $9, 20($sp)ld $10, 16($sp)slt $6, $6, $10subu $6, $2, $6slt $5, $5, $9subu $5, $2, $5slt $4, $4, $8subu $4, $2, $4slt $3, $3, $7subu $2, $2, $3st $2, 12($sp)st $4, 8($sp)st $5, 4($sp)st $6, 0($sp)ld $2, 12($sp)ld $2, 8($sp)ld $2, 4($sp)ld $2, 0($sp)ld $3, 12($sp)ld $3, 8($sp)ld $3, 0($sp)ld $3, 4($sp)addu $2, $2, $3ld $3, 12($sp)ld $3, 4($sp)ld $3, 0($sp)ld $3, 8($sp)addu $2, $2, $3ld $3, 8($sp)ld $3, 4($sp)ld $3, 0($sp)ld $3, 12($sp)addu $2, $2, $3ld $9, 56($sp) # 4-byte Folded Reloadld $10, 60($sp) # 4-byte Folded Reloadaddiu $sp, $sp, 64ret $lr
其实整体逻辑是很简单的。
2.9 cl指令
int countLeadingZero() {int a, b;b = __builtin_clz(a);return b;
}int countLeadingOne() {int a, b;b = __builtin_clz(~a);return b;
}
countLeadingZero:addiu $sp, $sp, -8ld $2, 4($sp)clz $2, $2st $2, 0($sp)ld $2, 0($sp)addiu $sp, $sp, 8ret $lrcountLeadingOne:addiu $sp, $sp, -8ld $2, 4($sp)clo $2, $2st $2, 0($sp)ld $2, 0($sp)addiu $sp, $sp, 8ret $lr相关文章:
LLVM Cpu0 新后端6
想好好熟悉一下llvm开发一个新后端都要干什么,于是参考了老师的系列文章: LLVM 后端实践笔记 代码在这里(还没来得及准备,先用网盘暂存一下): 链接: https://pan.baidu.com/s/1yLAtXs9XwtyEzYSlDCSlqw?…...
GAT1399协议分析(9)--图像上传
一、官方定义 二、wirechark实例 有前面查询的基础,这个接口相对简单很多。 请求: 文本化: POST /VIID/Images HTTP/1.1 Host: 10.0.201.56:31400 User-Agent: python-requests/2.32.3 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive content-type:…...
Spring ApplicationContext的getBean方法
Spring ApplicationContext的getBean方法 在Spring框架的ApplicationContext中,getBean(Class<T> requiredType)方法可以接受一个类类型参数,这个参数可以是接口类也可以是实现类。 使用接口类: 如果requiredType是一个接口,…...
自然语言处理(NLP)—— 自动摘要
自动摘要是一种将长文本信息浓缩为短文本的技术,旨在保留原文的主要信息和意义。 1 自动摘要的第一种方法 它的第一种方法是基于理解的,受认知科学和人工智能的启发。 在这个方法中,我们首先建立文本的语义表示,这可以理解为文本…...
Spring RestClient报错:400 Bad Request : [no body]
我项目采用微服务架构,所以各服务之间通过Spring RestClient远程调用,本来一直工作得好好的,昨天突然发现远程调用一直报错,错误详情如下: org.springframework.web.client.HttpClientErrorException$BadRequest: 400…...
【数据结构】 -- 堆 (堆排序)(TOP-K问题)
引入 要学习堆,首先要先简单的了解一下二叉树,二叉树是一种常见的树形数据结构,每个节点最多有两个子节点,通常称为左子节点和右子节点。它具有以下特点: 根节点(Root):树的顶部节…...
C#面:XML与 HTML 的主要区别是什么
C# XML与HTML有以下几个主要区别: 用途不同:XML(eXtensible Markup Language)是一种用于存储和传输数据的标记语言,它的主要目的是描述数据的结构和内容。HTML(HyperText Markup Language)是一…...
java并发-如何保证线程按照顺序执行?
【readme】 使用只有单个线程的线程池(最简单)Thread.join() 可重入锁 ReentrantLock Condition 条件变量(多个) ; 原理如下: 任务1执行前在锁1上阻塞;执行完成后在锁2上唤醒;任务…...
PyCharm中 Fitten Code插件的使用说明一
一. 简介 Fitten Code插件是是一款由非十大模型驱动的 AI 编程助手,它可以自动生成代码,提升开发效率,帮您调试 Bug,节省您的时间,另外还可以对话聊天,解决您编程碰到的问题。 前一篇文章学习了 PyCharm…...
Polar Web【简单】PHP反序列化初试
Polar Web【简单】PHP反序列化初试 Contents Polar Web【简单】PHP反序列化初试思路EXP手动脚本PythonGo 运行&总结 思路 启动环境,显示下图中的PHP代码,于是展开分析: 首先发现Easy类中有魔术函数 __wakeup() ,实现的是对成员…...
树莓派4B 零起点(二) 树莓派 更换软件源和软件仓库
目录 一、准备工作,查看自己的树莓派版本 二、安装HTTPS支持 三、更换为清华源 1、更换Debian软件源 2,更换Raspberrypi软件仓库 四、进行软件更新 接前章,我们的树莓派已经启动起来了,接下来要干的事那就是更换软件源和软件…...
Pytorch 实现目标检测二(Pytorch 24)
一 实例操作目标检测 下面通过一个具体的例子来说明锚框标签。我们已经为加载图像中的狗和猫定义了真实边界框,其中第一个 元素是类别(0代表狗,1代表猫),其余四个元素是左上角和右下角的(x, y)轴坐标(范围…...
如何使用Python中的列表解析(list comprehension)进行高效列表操作
Python中的列表解析(list comprehension)是一种创建列表的简洁方法,它可以在单行代码中执行复杂的循环和条件逻辑。列表解析提供了一种快速且易于阅读的方式来生成新的列表。 以下是一些使用列表解析进行高效列表操作的示例: 1.…...
java使用websocket遇到的问题
java使用websocket的bug 1 websocket连接正常但是收不到服务端发出的消息java的websocket并发的时候导致连接断开(看着连接是正常的,但是实际上已经断开) 1 websocket连接正常但是收不到服务端发出的消息 java的websocket并发的时候导致连接断…...
[Cloud Networking] Layer 2
文章目录 1. 什么是Mac Address?2. 如何查找MAC地址?3. 二层数据交换4. [Layer 2 Protocol](https://blog.csdn.net/settingsun1225/article/details/139552315) 1. 什么是Mac Address? MAC 地址是计算机的唯一48位硬件编码,嵌入到网卡中。 MAC地址也…...
[240609] qwen2 发布,在 Ollama 已可用 | 采用语言模型构建通用 AGI(2020年8月)
目录 qwen2 发布,在 Ollama 已可用Qwen2 模型概览 (基于 Ollama 网站信息)一、模型介绍二、模型参数三、支持语言 (除英语和中文外)四、模型性能五、许可证六、数据支撑: 采用语言模型构建通用 AGI qwen2 发布,在 Ollama 已可用 Qwen2 模型概览 (基于 O…...
赶紧收藏!2024 年最常见 20道分布式、微服务面试题(五)
上一篇地址:赶紧收藏!2024 年最常见 20道分布式、微服务面试题(四)-CSDN博客 九、在分布式系统中,如何保证数据一致性? 在分布式系统中保证数据一致性是一个复杂的问题,因为分布式系统由多个独…...
为什么Kubernetes(K8S)弃用Docker:深度解析与未来展望
为什么Kubernetes弃用Docker:深度解析与未来展望 🚀 为什么Kubernetes弃用Docker:深度解析与未来展望摘要引言正文内容(详细介绍)什么是 Kubernetes?什么是 Docker?Kubernetes 和 Docker 的关系…...
软件游戏提示msvcp120.dll丢失的解决方法,总结多种靠谱的解决方法
在电脑使用过程中,我们可能会遇到一些错误提示,其中之一就是“找不到msvcp120.dll”。那么,msvcp120.dll是什么?它对电脑有什么影响?有哪些解决方法?本文将从以下几个方面进行探讨。 一,了解msv…...
使用kafka tools工具连接带有用户名密码的kafka
使用kafka tools工具连接带有用户名密码的kafka 创建kafka连接,配置zookeeper 在Security选择Type类型为SASL Plaintext 在Advanced页面添加如下图红框框住的内容 在JAAS_Config加上如下配置 需要加的配置: org.apache.kafka.common.security.plain.Pla…...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
